今天来看一个比较复杂的排序,堆排序,先搞清楚原理,再写代码。
堆排序使用数据结构中的堆来完成,那么问题来了,什么是堆?
有两种,大顶堆和小顶堆,所谓大顶堆,就是任意一个双亲节点都比孩子节点大,比如图上这样的,小顶堆则反过来。
所以对于大顶堆来说,根节点一定是最大的。
比如有这样一个数组,先把它画成一颗二叉树的形式,接下来就是构建大顶堆,这个部分也是整个堆排序中最耗时的部分。
从0这个节点开始,因为节点0是最后一个有孩子的节点。
0比2小,交换一下位置。
再轮到4,8和9比较,显然是9大,把9和4交换一下位置。
最后轮到3,9比2大,9和3需要交换一下位置。
注意,因为这个节点发生了变化,所以3 8 4不再满足条件,还得继续调整。8比4大,8和3交换一下位置。
这个过程就是构建大顶堆。
于是,我们得到了最大的数字9,把它和最后一个数字做交换,然后断开,意思就是后面的操作跟9没有关系了。
接下来就是调整大顶堆,不需要再像刚才那样构建,因为这颗二叉树也只有根节点被修改了。
0和8交换,4和0交换,又得到了第二大的数字8。
不断的重复,最后就是一个有序的序列。
写代码之前,还得明确一个问题,虽然我们一直在操作二叉树,但是写代码的时候并不需要真的去创建一颗二叉树,我们只是在操作数组的下标,比如下标为n的节点作为根几点,那么他的左孩子就是 2n+1,右孩子就是2n+2。
#include代码确实不太容易理解,不过在这些排序算法中,越是不容易理解的,效率也就越高,还是用它和冒泡做个对比,10000个数据,差距还是很大的。void adjust_heap_sort(int *a, int root, int last) { int child; while (1) { child = 2 * root + 1; if (child > last) break; if (child + 1 <= last && a[child] < a[child + 1]) { child++; } if (a[child] > a[root]) { int num = a[root]; a[root] = a[child]; a[child] = num; } root = child; } } void heap_sort(int *a, int size) { //构建大顶堆 for (int i = size / 2 - 1; i >= 0; i--) { adjust_heap_sort(a, i, size - 1); } //调整大顶堆 for (int i = size - 1; i > 0; i--) { int num = a[0]; a[0] = a[i]; a[i] = num; adjust_heap_sort(a, 0, i - 1); } } int main() { int array[] = {3, 4, 0, 8, 9, 2}; heap_sort(array, 6); for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++) { printf("%d ", array[i]); } printf(" "); return 0; }
root@Turbo:test# time ./heap_sort real 0m0.005s user 0m0.004s sys 0m0.000s root@Turbo:test# time ./bubble_sort real 0m0.606s user 0m0.554s sys 0m0.000s root@Turbo:test#
审核编辑:刘清
全部0条评论
快来发表一下你的评论吧 !