大体思想:
将长度为N的序列依次完成长度为1的有序序列,长度为2的有序序列.长度为4的有序序列...长度为N的有序序列,如下表所示:
具体实现:
具体是用递归方式实现的,其中递归中的关键一步为;若一个序列的左序列和右序列均已经排好序列,就将其合并。
合并部分为借助一个与待合并序列长度之和的临时数组,首先利用双指针法将两个有序的子序列排好序放入临时数组中,然后将临时数组中的数据复制到原始数组中。
具体代码如下:
public static void merge(int[] data,int[] tempData,int left,int right,int rEnd) {
//把拍好序的元素放到tempData中
int i=left,j=right,k=left;
while(i<right&&j<=rEnd) {
if(data[i]>data[j]) {
tempData[k++]=data[j++];
}else {
tempData[k++]=data[i++];
}
}
while(i<right) {//说明此时右边的已经全部挪到tempData中了
tempData[k++]=data[i++];
}
while(j<=rEnd) {//说明此时左边的已经全部挪到tempData中了
tempData[k++]=data[j++];
}
for(int m=left;m<=rEnd;m++) {
data[m]=tempData[m];
}
}
public static void mergeSort(int[] data,int[] tempData,int start,int end) {
if(start<end) {
int center=(start+end)/2;
mergeSort(data,tempData,start,center);
mergeSort(data,tempData,center+1,end);
merge(data,tempData,start,center+1,end);
}
}
注:该代码中并没有在每次迭代的合并过程中申请与待合并数组相同大小的空间而是在外面直接一次性给他分配N的空间,后续操作只依赖下标即可。
这是由于前者的做法需在每次执行merge时不断 分配空间结束时并且释放(虽然JVM帮你处理了),但是不断释放和分配会耗费时间,此外即使这样在最后一次合并中依然需要N的空间。上述做法总的空间复杂度依然为O(N)(虽然迭代过程中的额为空间占用为依次为1,2,4...N)。
复杂度:
很显然每次迭代过程中的时间复杂度为O(N),其又需要logN次,因此其时间复杂度为O(NlogN)。
其空间复杂度为O(N).
此外,由于合并的过程中关键字相同的两个元素并不会改变其的相对位置。