题意描述:
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
- 初始化 nums1 和 nums2 的元素数量分别为 m 和 n。
- 你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
解题思路:
Alice: 这个简单啊,直接把nums2数组里面的元素复制到nums1里面,然后一起再排个序就好了。
Bob: 哦豁,可以这样写,但是这样时间复杂度就是O(n*log(n))了,应该还有更快的写法,两个数组都是有序的这个信息要用起来呀。
Alice: 两个数组都是有序的,排序的话应该会比“最坏情况”快不少吧。
Bob:我们可以遍历nums2中的元素,为每个元素找到一个插入的位置,然后右移nums1里面插入位置后面的元素,再插入。
Alice: 你这样每次都将后面的元素都右移一位,应该比排序更耗时吧。应该是O(n^2)了。
Bob: 是啊,要是能不右移一位,一次性找到所有元素的最终位置就好了。该怎么做呢?
Alice:双指针啊,就像那个什么合并两个有序链表一样。再开一块内存,放一个新数组,把两个数组合并,然后再把合并后的数组赋值到 nums1 的地址。
Bob: 这样跑起来是很快的O(m+n)的时间复杂度,也就遍历了两个数组,再加上最后赋值的一个遍历。
Alice: emmmm, 这样看来应该还有更好的解法,直接把nums2的数组元素插入到nums1的指定位置上去,这样就没有后面再复制数组的操作了。
Bob: 是啊,可是该怎么写呢?怎么找到准确的插入位置呢?可以直接计算出来吗?
Alice: 明儿再说吧,我妈喊我回家吃饭了。
代码:
Python 偷懒的方法:直接将nums2中的元素复制到nums1后面的位置,然后排了个序,排序的算法还不是自己写的。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
for x in range(m, len(nums1)):
nums1[x] = nums2[x-m]
nums1.sort()
return
Python 解法二:遍历nums2中的每一个元素,为每一个元素在nums1中找到要插入的位置,然后将插入位置后面的所有元素都右移一位,然后插入元素。写起来费事费力,算是对复杂逻辑的一种考验吧 ?
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
endIndex = m-1
for ele in nums2:
insertPosition = self.findPosition(nums1, endIndex, ele)
endIndex += 1
# 每次插入一个元素后,nums1里面有序数组的长度就要增加一个
nums1 = self.rightShiftOne(nums1, insertPosition)
nums1[insertPosition] = ele
return
def findPosition(self, nums: List[int], endIndex: int, ele: int) -> int:
# 在有序数组nums[:endIndex+1] 中寻找一个 ele 插入的位置。注意 ele 很小或者 ele 很大的情况。
if ele <= nums[0]:
return 0
if ele >= nums[endIndex]:
return endIndex + 1
for x in range(1, endIndex+1):
if nums[x-1] <= ele and nums[x] > ele:
return x
def rightShiftOne(self, nums: List[int], startIndex: int) -> List[int]:
# 将nums数组中从startIndex下标开始的元素全部右移一位
endIndex = len(nums) - 1
for x in range(endIndex, startIndex, -1):
nums[x] = nums[x-1]
return nums
Python 解法三: 双指针法,不过我用了额外的 (m+n) 内存,时间复杂度当然很低了,只有O(m+n),是的,仅仅遍历了两个数组而已。但是,应该还有直接将nums2数组中的元素插入到nums1中指定位置的算法,将来补上吧。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
ret = []
index1 = index2 = 0
while index1 < m or index2 < n:
if index1 == m and index2 < n:
ret.extend([nums2[x] for x in range(index2, n)])
break
if index1 < m and index2 == n:
ret.extend([nums1[x] for x in range(index1, m)])
break
if nums1[index1] <= nums2[index2]:
ret.append(nums1[index1])
index1 += 1
else:
ret.append(nums2[index2])
index2 += 1
for x in range(m+n):
nums1[x] = ret[x]
return
Python 解法三优化:还是双指针,使用了更少的内存。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
tmp = [nums1[x] for x in range(m)]
index1 = index2 = index = 0
while index1 < m and index2 < n:
if tmp[index1] <= nums2[index2]:
nums1[index] = tmp[index1]
index1 += 1
else:
nums1[index] = nums2[index2]
index2 += 1
index += 1
if index1 < m:
nums1[index: index + m-index1] = tmp[index1:m]
if index2 < n:
nums1[index: index + n-index2] = nums2[index2:n]
return
Python 方法四: 双指针,但是从数组末尾开始插入,不需要额外的空间,时间复杂度O(n)。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
index1 = m-1
index2 = n-1
index = m + n - 1
while index1 >= 0 and index2 >= 0:
if nums1[index1] >= nums2[index2]:
nums1[index] = nums1[index1]
index1 -= 1
else:
nums1[index] = nums2[index2]
index2 -= 1
index -= 1
if index2 >= 0:
nums1[:index+1] = nums2[:index2+1]
return
Java 方法一: 拼接 + 排序
class Solution {
public void merge(int[] A, int m, int[] B, int n) {
int aindex = m + n - 1;
int bindex = n - 1;
while(bindex >= 0){
A[aindex--] = B[bindex--];
}
Arrays.sort(A);
}
}
Java 方法二: 终极解法,O(n),反个方向就好了。有点像剑指 Offer 里面替换字符那道题的思路。
class Solution {
public void merge(int[] A, int m, int[] B, int n) {
int index = m + n - 1;
n--;
m--;
while(n >= 0 && m >=0){
if(A[m] > B[n]){
A[index--] = A[m--];
}else{
A[index--] = B[n--];
}
}
while(n >= 0){
A[index--] = B[n--];
}
}
}
易错点:
- 还是边界值和一些特殊情况:
[400,0, 0]
1
[1, 3]
2
[0, 0, 0]
0
[1, 3]
2
[1, 0, 0]
1
[2, 3]
2
总结:
- 另一些一些测试用例。
[1,2,3,0,0,0]
3
[2,5,6]
3
[1,0]
1
[1]
1
[]
0
[]
0
[12]
1
[]
0
[0]
0
[1]
1