在lintcode中关于合并排序数组有两道题目:
1. 合并两个排序的整数数组A和B变成一个新的数组。给出A=[1,2,3,4],B=[2,4,5,6],返回 [1,2,2,3,4,4,5,6]
2. 合并两个排序的整数数组A和B变成一个新的数组。给出A = [1, 2, 3, empty, empty] B = [4,5],合并之后A将变成[1,2,3,4,5]。其中,假设数组A有足够大的空间,也就是说,如果A的非空元素个数为m,B的非空元素个数为n,那么A的空间容量一定是大于等于m+n的
我们来看这两个题目,其实求解的东西一样,不同在于,第一个问题是新建一个排序好的新数组,而第二题是将一个数组合并到另一个数组中去。怎样做呢?两个题的基本思路都一样:通过两个指针同时扫描两个数组。
先来看第一题,我们通过设置两个指针(此处就用数组元素的下标来表示)分别扫描A,B。同时,在开始阶段建立一个新的空数组result=[],作为结果输出。扫描过程中,不断将数组元素按照下列规则“push”进result:
(1)如果A[i] > B[j],将B[j]压入result,同时令j = j + 1(扫描B的指针向后移一位)
(2)如果A[i] < B[j],将A[i]压入result,同时令i=i+ 1(与上步做法一致)
(3)如果其中一个数组扫描完了,而另一个还没完,那么将没扫描完的数组的剩余部分全部压入result
因为A,B都是排好序的,所以通过以上规则,我们可以得到合并后的,排好序的数组result。
Python代码如下:
class Solution:
"""
@param A: sorted integer array A
@param B: sorted integer array B
@return: A new sorted integer array
"""
def mergeSortedArray(self, A, B):
# write your code here
nA = len(A)
nB = len(B)
res = []
index_a = index_b = 0
#两个指针都不能越界
while index_a != nA and index_b != nB:
if A[index_a] < B[index_b]:
res.append(A[index_a])
index_a += 1
else:
res.append(B[index_b])
index_b += 1
#与未被扫描的部分合并,因为都是排好序的数组,所以直接相加
res += A[index_a:]
res += B[index_b:]
return res
这个还是很容易理解的。如果这道题没问题了,就可以看第二个问题,要求把两个数组合并成一个,而且给出了两个数组非空元素的数目:m,n。首先想这样一个问题,两个数组合并后的非空元素个数一定是m+n。也就是说,如果输出的结果数组(题中的意思就是A了)是这种形式:[*,*,*,*, empty, empty...empty]的话(*表示非空元素),那么最后一个非空元素的下标为m+n-1(下标从0开始)。那就可以考虑分别设两个指针从后往前扫描A,B数组,并且从A[m+n-1]这个位置开始,再设置一个指针,从后往前给A的元素重新赋值。扫描的规则与第一题同理,不同在于因为是从后往前扫描,所以按照“谁大谁先赋值”的规则,举题目的例子如下:
A = [1, 2, 3, empty, empty]
B = [4,5]
m = 3
n = 2
m + n - 1 = 4
所以,从A[4]这个位置,向前开始赋值:
(1)先比较A[2] = 3和B[1] = 5,因为B[1] > A[2],所以令A[4] = B[1] = 5
(2)比较B[0]和A[2],大者赋值给A[3]
(3)按照这个规则持续扫描,直到其中一个扫描完为止
可见,之所以采取从后往前,而不是从前往后,是为了避免大量元素移位造成的运算量。
但是,接下来,我们要分析这么个事:其中一个扫描完了怎么办?
如果是A先扫描完,可以想象,情况是这样:假如A = [4, 5, empty, empty, empty],B = [1, 2, 6],A一定先扫描完,此时A = [4, 5, 4, 5, 6],B = [1, 2, 6],那么须要将B的剩余部分[1, 2]分别赋值给A的还没赋值的部分;
而如果是B先扫描完了,那情况就简单了,我们不需任何操作(因为A前面没有扫描的部分也是排好序的)。
直接看代码吧,也许就能理解了:
class Solution:
"""
@param: A: sorted integer array A which has m elements, but size of A is m+n
@param: m: An integer
@param: B: sorted integer array B which has n elements
@param: n: An integer
@return: nothing
"""
def mergeSortedArray(self, A, m, B, n):
# write your code here
total = m+n-1
while m > 0 and n > 0:
if A[m-1] > B[n-1]:
A[total] = A[m-1]
m -= 1
else:
A[total] = B[n-1]
n -= 1
total -= 1
#如果B没有扫描完,就将剩余的B全部放在A的前面
if n > 0:
A[:total+1] = B[:n]
return A
其实很简单的,我觉得反倒是让我说复杂了,没办法,表达能力实在有限。。。那么问题来了,为什么在这里讲了两道lintcode的题呢,是为了下一节归并排序做准备。