level3,下面介绍几种稳定的排序方式,主要是两种,一个是堆排序(也叫做锦标赛排序),第二个就是我们经常用的二路归并排序,第三个就是多路归并排序(JDK)
当时在学校的时候觉得,快速排序真的是一个很好的想法,后来又看到了令人巧妙到让人不理解的一些思想。
下面说下堆排序,介绍原理,
其别名很形象,锦标赛,也就是一个二叉树状比赛,赢了就出去,下面继续打。
这个打的过程称做堆重构,赢的数字就是极值,这种比较和交换,绝对是超稳定的,因为只有两个点。
如下数字:64,96,19,52,85,71,64,48,1,78
堆排序也是分治的一种,堆重构也是小方法体之一,巧妙在它对于一个数组的理解,把一个数组当作一个树来比较了。。
64
96 19
52 85 71 64
48 1 78
这里假设四个变量 block 数组,start,end,哨兵p,start,end为数组位置,不是地址索引,即start=index+1,在java和c里面是如此的。
p开始初始化为数组第一个,block[start-1], 即是root
它是怎么做的呢,首先,从i=start*2 开始,这里就是96,然后和19比较,如果96>10,那么和block[start-1]互换一下,保证
block[start-1]是堆的胜出点,
然后再判断i,这个是否到了尾部,如果不是,那么当前i=i*2,刚才的i是2,所以现在的i=4了,52和84比较,小,pass,
前进直到找到最大,85,block[i-1],下面是同理,
最后返回这个堆极值点。
堆重构:
private static void HeapAdjust(int arr[], int start, int end) {
int temp = arr[start - 1];
for (int i = start<<1; i <= end; i = i<<1) {
if (i < end && (arr[i - 1] < arr[i]))
i++;
if (temp >= arr[i - 1])
break;
arr[start - 1] = arr[i - 1];
start = i;
}
arr[start - 1] = temp;
}
好了。。。
下面定义大方法体,
相当简单,将构造的堆的胜者一个一个放在数组后面。。
代码如下:
public static void HeapSort(int arr[]) {
int temp;
int len = arr.length;
for (int i = len >>1; i > 0; i--)
HeapAdjust(arr, i, len);
for (int j = len; j > 1; j--) {
temp = arr[0];
arr[0] = arr[j - 1];
arr[j - 1] = temp;
HeapAdjust(arr, 1, j - 1);
}
}
测试一下:
100*10000 ,execute time:250
1000*10000 ,execute time:2735
发现在运行的时候好像有点慢,其实这是因为构建堆的时候是比较耗资源的,
为了突出其稳定性,再执行几次。
1000*10000 ,execute time:2750
1000*10000 , execute time:2859
1000*10000, execute time:2687
execute time:2735
首先快速是在数量级中等时是比较适合的,1000W级别并不很适合。
然后快速在100W级别的时间,看看其稳定性:
1. execute time:8141
2. execute time:8360
3.execute time:8469
4.execute time:8250
首先,这两个是不在一个数量级,但是1000W能精确到200ms左右,而quickSort 100W是在300ms浮动,
还有稳定会带一定的好处就是,减少交换的次数,这点在这两者之间体现的相当明显。