双指针法的解决思路:
- 初始化两个指针:分别指向两个有序集合的起始位置。
- 比较当前指针指向的元素:
- 如果集合1的元素小于集合2的元素,将集合1的元素放入结果集中,并将集合1的指针向后移动。
- 如果集合2的元素小于集合1的元素,将集合2的元素放入结果集中,并将集合2的指针向后移动。
- 如果两个集合的元素相等,则可以选择将其中一个元素加入结果集,然后两个指针都向后移动。
- 处理剩余元素:当其中一个集合的所有元素都已合并到结果集中,另一集合可能还有剩余元素。此时直接将剩余元素加入结果集中。
- 返回结果集:所有元素被合并后返回结果。
代码:
package main
import "fmt"
func mergeSet(set1, set2 []int) []int {
result := []int{}
i, j := 0, 0
for i < len(set1) && j < len(set2) {
// 1. 比较大小,小的先走
if set1[i] < set2[j] {
result = append(result, set1[i])
i++
} else if set1[i] > set2[j] {
// 2. 大的后走
result = append(result, set2[j])
j++
} else {
// 3. 相等时候插入一个,指针都走一步
result = append(result, set1[i])
result = append(result, set2[j])
i++
j++
}
}
// 如果有剩下的
for i < len(set1) {
result = append(result, set1[i])
i++
}
for j < len(set2) {
result = append(result, set2[j])
j++
}
return result
}
func main() {
// 集合1
a := []int{1,2,3,4,5}
// 集合2
b := []int{3,4,5,6,7}
result := mergeSet(a, b)
fmt.Println(result)
}
时间复杂度:
- 双指针法的时间复杂度为O(n+m),其中n和m分别为两个集合的长度。因为每个集合的每个元素只会被遍历一次,所以整体时间复杂度是线性级别的。
合并逻辑调整:一轮for循环实现方式
我们可以将双指针逻辑合并为一个更简洁的单个for循环,进一步简化代码。通过调整循环条件,可以避免使用两个单独的for循环处理剩余元素的逻辑。
优化后的代码:
package main
import "fmt"
func mergeSetOneLoop(set1, set2 []int) []int {
result := []int{}
i, j := 0, 0
// 一个循环内处理合并
for i < len(set1) || j < len(set2) {
if i < len(set1) && (j >= len(set2) || set1[i] < set2[j]) {
result = append(result, set1[i])
i++
} else if j < len(set2) && (i >= len(set1) || set1[i] > set2[j]) {
result = append(result, set2[j])
j++
} else {
result = append(result, set1[i])
i++
j++
}
}
return result
}
func main() {
// 集合1
a := []int{1,2,3,4,5}
// 集合2
b := []int{3,4,5,6,7}
result := mergeSetOneLoop(a, b)
fmt.Println(result) // [1 2 3 3 4 4 5 5 6 7]
}
代码解释:
- 使用一个
for循环控制流程,条件为i < len(set1) || j < len(set2),即当任意一个集合没有遍历完时,继续合并。 - 在循环中判断:
- 当
i没有超出set1的长度且j已经超出set2的长度,或者set1[i]小于set2[j]时,将set1[i]添加到结果中,并移动i。 - 同理,处理
set2中的元素。 - 当
set1[i]和set2[j]相等时,将set1[i]或set2[j](二者相等)添加到结果集中,并同时移动两个指针。
- 当
时间复杂度:
- 单循环法的时间复杂度依然是O(n+m),因为每个元素只被处理一次,整体算法的时间复杂度是线性的。
两种实现方式的对比
| 解决方法 | 时间复杂度 | 优点 | 缺点 |
|---|---|---|---|
| 双指针法(两个for循环) | O(n + m) | 逻辑清晰、代码结构分明,容易理解 | 使用多个for循环,代码稍微冗长 |
| 单循环法 | O(n + m) | 代码简洁,合并剩余元素时不需要额外的循环 | 条件判断稍显复杂,理解门槛较高 |