基本思想
是利用归并的思想进行排序的放法,它的原理是假设初始序列含有n个记录,则可以看成n个有序的子序列,每个子序列的长度为1,然后两两归并,得到 n/2 个长度为2或者1的有序子序列,然后在两两归并,直到得到一个长度为n的有序序列为止。
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
---|---|---|---|---|---|
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |
待排序的记录序列中可能存在两个或两个以上关键字相等的记录。排序前的序列中
Ri
领先于
Rj
,即
i<j
,若在排序后的序列中
Ri
仍然领先于
Ri
,则称所用的方法是稳定的。
稳定的排序有:插入排序、基数排序、归并排序、冒泡排序、计数排序
不稳定的排序有:快速排序、希尔排序、简单选择排序、堆排序
代码实现
public static void sort(int[] data, int left, int right) {
if(left<right){
//找出中间索引
int center=(left+right)/2;
//对左边数组进行递归
sort(data,left,center);
//对右边数组进行递归
sort(data,center+1,right);
//合并
merge(data,left,center,right);
}
}
public static void merge(int[] data, int left, int center, int right) {
int[] tmpArr=new int[data.length];
int mid=center+1;
//third记录中间数组的索引
int third=left;
int tmp=left;
while(left<=center&&mid<=right){
//从两个数组中取出最小的放入中间数组
if(data[left]<=data[mid]){
tmpArr[third++]=data[left++];
}else{
tmpArr[third++]=data[mid++];
}
}
//剩余部分依次放入中间数组
while(mid<=right){
tmpArr[third++]=data[mid++];
}
while(left<=center){
tmpArr[third++]=data[left++];
}
//将中间数组中的内容复制回原数组
while(tmp<=right){
data[tmp]=tmpArr[tmp++];
}
}
数据交换示意图
代码优化
上文的代码使用递归的方式,尽管代码清晰而且比较容易理解,但是将会消耗大量的时间和空间,下面实现一下非递归的版本,这应该是自底向上的归并。一开始的归并长度为1,以后每次加倍,归并的时候要注意处理归并段的长度为奇数和的情况和最后一个归并段的长度和前面的不等的情况,需要做一下处理。
public static void sortNo(int[] A){
int len = 1;
int low = 0;
int mid;
int high;
// 程序边界的处理非常重要
while (len <= A.length) {
for (int i = 0; i + len <= A.length - 1; i += len * 2) {
System.out.println("i="+i);
System.out.println("len="+len);
low = i;
mid = i + len - 1;
high = i + len * 2 - 1;
//最后一个为奇数的情况
if (high > A.length - 1)
high = A.length - 1;
merge(A, i, mid, high);
System.out.println(Arrays.toString(A));
}
//长度加倍
len += len;
}
}
以{50,10,90,30,70,40,80,60,20}为例
参考资料
[1] 大话数据结构 清华大学出版社 程杰