题干:
给你两个按 非递减顺序 排列的整数数组 nums1
和 nums2
,另有两个整数 m
和 n
,分别表示 nums1
和 nums2
中的元素数目。
请你 合并 nums2
到 nums1
中,使合并后的数组同样按 非递减顺序 排列。
注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1
中。为了应对这种情况,nums1
的初始长度为 m + n
,其中前 m
个元素表示应合并的元素,后 n
个元素为 0
,应忽略。nums2
的长度为 n
。
解法1:比较易懂,心智负担很低,但应该不是出题本意
func merge(nums1 []int, m int, nums2 []int, n int) {
copy(nums1[m:], nums2)
sort.Ints(nums1)
}
解析1:
-
copy(nums1[m:], nums2)
:copy
函数用于将nums2
的元素复制到nums1
中从索引m
开始的位置。nums1[m:]
表示nums1
从索引m
开始到末尾的切片。nums2
是源切片,copy
函数会将nums2
中的所有元素复制到nums1[m:]
中。
-
sort.Ints(nums1)
:sort.Ints
函数用于对nums1
进行排序,使其按非递减顺序排列。sort.Ints
是 Go 语言标准库中的一个函数,用于对整数切片进行排序。
- 时间复杂度
copy
函数的时间复杂度是 O(n),其中 n 是nums2
的长度。sort.Ints
函数的时间复杂度是 O(m + n) * log(m + n)),其中 m 和 n 分别是nums1
和nums2
的有效元素个数。-
总体时间复杂度是 O((m + n) * log(m + n)),这比双指针法的 O(m + n) 要高。
- 空间复杂度
- 由于
copy
和sort.Ints
都是原地操作,不需要额外的空间,所以空间复杂度是 O(1)。 -
虽然这种方法简单易懂,但在处理大规模数据时,双指针法会更加高效
- 由于
解法2:双指针法
func merge(nums1 []int, m int, nums2 []int, n int) {
for p1, p2, tail := m-1, n-1, m+n-1; p1 >= 0 || p2 >= 0; tail-- {
var cur int
if p1 == -1 {
cur = nums2[p2]
p2--
} else if p2 == -1 {
cur = nums1[p1]
p1--
} else if nums1[p1] > nums2[p2] {
cur = nums1[p1]
p1--
} else {
cur = nums2[p2]
p2--
}
nums1[tail] = cur
}
}
解析2:
-
初始化指针:
p1
指向nums1
中最后一个有效元素的位置,即m - 1
。p2
指向nums2
中最后一个有效元素的位置,即n - 1
。tail
指向nums1
的末尾,即m + n - 1
。
-
循环条件:
- 循环继续的条件是
p1 >= 0 || p2 >= 0
,即只要nums1
或nums2
中还有未处理的元素,循环就会继续。
- 循环继续的条件是
-
选择当前元素:
- 使用
var cur int
声明一个变量cur
来存储当前要放入nums1[tail]
的元素。 - 如果
p1 == -1
,说明nums1
中的元素已经全部处理完毕,此时直接从nums2
中取元素,并将p2
向前移动一位。 - 如果
p2 == -1
,说明nums2
中的元素已经全部处理完毕,此时直接从nums1
中取元素,并将p1
向前移动一位。 - 如果
nums1[p1] > nums2[p2]
,说明nums1
中的当前元素较大,将其放入nums1[tail]
,并将p1
向前移动一位。 - 否则,说明
nums2
中的当前元素较大或两者相等,将其放入nums1[tail]
,并将p2
向前移动一位。
- 使用
-
填充
nums1
:- 将选定的
cur
放入nums1[tail]
,然后将tail
向前移动一位。
- 将选定的
- 时间复杂度
- 每个元素只会被处理一次,因此时间复杂度是 O(m + n)。
- 空间复杂度
- 只使用了常数级别的额外空间,因此空间复杂度是 O(1)。