题目来自LeetCode,链接:面试题 10.01. 合并排序的数组。具体描述为:给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。初始化 A 和 B 的元素数量分别为 m 和 n。(其中A.length == n + m)
示例:
输入:
A = [1,2,3,0,0,0], m = 3
B = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
一开始想到的方法是先用一个长度为m的数组C存放真正的A数组,其实就是索引从0到m-1的A数组,然后用两个指针index1和index2分别指向C和B的开头,比较C[index1]和B[index2]的大小,将小的填充到A数组里面去,直至B和C中有一个被全部填充到A里面去,然后再将另一个数组还剩下的数一股脑填充到A剩下的空间里去。这么做的时间复杂度是 ,空间复杂度则为 。
但其实因为A数组后面有一段长度为n的都是空着的,说明我们有可能做到不额外开辟空间的,也就是空间复杂度可以做到 。做法也很简单,就是我们不从左到右填充A数组了,改为从右往左。具体做法如下:
- 初始化两个指针index1=m-1和index2=n-1分别指向排序数组A和B的最后一个元素
- 比较两个指针指向的数组的大小,把较大者填充到A数组中索引为index1+index2+1的位置,同时较大者指针左移,直至有一方的指针移到越界
- 如果是A数组的指针越界,说明B数组还剩下若干数未移到A数组,直接全部移过去(如果是B的越界,则不用再操作了,因为自然现在A在index1前面的数都是排好序的了)
这样时间复杂度还是 ,但空间复杂度降到了 。
JAVA版代码如下:
class Solution {
public void merge(int[] A, int m, int[] B, int n) {
int index1 = m - 1, index2 = n - 1;
while (index1 >= 0 && index2 >= 0) {
if (A[index1] > B[index2]) {
A[index1 + index2 + 1] = A[index1];
--index1;
}
else {
A[index1 + index2 + 1] = B[index2];
--index2;
}
}
if (index1 == -1) {
while (index2 >= 0) {
A[index2] = B[index2];
--index2;
}
}
}
}
提交结果如下:
Python版代码如下:
class Solution:
def merge(self, A: List[int], m: int, B: List[int], n: int) -> None:
"""
Do not return anything, modify A in-place instead.
"""
index1, index2 = m - 1, n - 1
while index1 >=0 and index2 >= 0: #从后往左填充A数组
if A[index1] > B[index2]: #当前A元素大于B元素,则填充A元素,A的索引左移
A[index1 + index2 + 1] = A[index1]
index1 -= 1
else: #否则需要填充B元素,同样B的索引左移
A[index1 + index2 + 1] = B[index2]
index2 -= 1
if index1 == -1: #B的元素还有部分未填充,全部填到A的头部去
while index2 >= 0:
A[index2] = B[index2]
index2 -= 1
提交结果如下: