排序算法(七)-堆排序
2017-09-17 17:12
211 查看
1. 原理
1.1 什么是堆要理解堆排序,首先要先理解什么是堆。堆是一颗顺序存储的完全二叉树,堆又分为最大堆和最小堆。
完全二叉树:设二叉树的深度为h,除h层外,其他各层的节点数都达到最大个数,第h层的所有节点都连续集中在最左边。
最小堆:每个节点的值都不大于其子节点的值的完全二叉树
最小堆:每个节点的值都不小于其子节点的值的完全二叉树
根据上面的描述我们可以用一个数学描述来定义最大最小堆:
对于数组[D(0), D(1), …., D(n)]当且仅当满足下列关系时称之为堆
最小堆:D(i) <= D(2 * i + 1)并且D(i) <= D(2 * i + 2)
最大堆:D(i) >= D(2 * i + 1)并且D(i) >= D(2 * i + 2)
其中整数i = 0, 1, 2, …, n/2
举个栗子:[3, 4, 7, 12, 15, 18]就是一个典型的最小堆, i <= 2。
i取0时:3 > 4, 3 > 4
i取1时:4 > 12, 4 > 15
i取2时:7 > 18
1.2 堆排序
理解了堆的概念之后,堆排序就是利用最大堆和最小堆的特性进行排序
将数组初始化成最大堆
交换最大堆的第一个和最后一个数字,输出最后一个数字(最大值)
将破坏后的最大堆重新调整为最大堆
接着重复2~3步直到交换堆的第一和第二个节点,此时结束排序
2. 实现
此算法的关键在于如何构建最大堆。根据上诉最大最小堆的定义,可以写出针对某一parent的调整算法。public void adjustMaxHeap(int[] data, int parent, int length) { int temp = data[parent]; int child = 2 * parent + 1; while (child < length) { //如有有右子节点,并且右子节点大于左子节点,则选取右子节点和parent比较 if (child + 1 < length && data[child] < data[child + 1]) { child++; } //如果父节点的值大于子节点的值,则跳出循环 if (temp >= data[child]) { break; } //把孩子节点的值赋给父节点 data[parent] = data[child]; //选取子节点的左子节点,继续向下调整 parent = child; child = 2 * child + 1; } data[parent] = temp; }
此函数为调整某个parent的值,初始化的时候需要从n/2开始一直循环调整到0;
for (int i = data.length / 2; i >= 0; i--) { adjustMaxHeap(data, i); }
完整对堆排序算法如下:
@Override public int[] sort(int[] data) { if (data == null || data.length <= 1) { return data; } for (int i = data.length / 2; i >= 0; i--) { adjustMaxHeap(data, i, data.length); } for (int i = data.length - 1; i > 0; i--) { //最后一个数字和第一个数字交换 swap(data, 0, i); //由于parent=0的节点发生了变化,因此需要重新调整堆 adjustMaxHeap(data, 0, i); } return data; }
完整实现可查看:
GitHub/HeapSort
3. 复杂度
堆排序是一种不稳定的排序算法。平均,最坏和最好的时间复杂度都是O(nlogn)。相关文章推荐
- 16 - 12 - 17 十大排序算法总结(二) 之 桶排序,堆排序
- 八种常见排序算法:插入、冒泡、选择、希尔、归并、快排、堆排序、基数排序
- 排序算法:堆排序
- 排序算法-堆排序
- 排序算法之堆排序详解(附最大堆示例代码)
- 排序算法6——堆排序
- 排序算法(2)——堆排序
- 排序算法之 堆排序 及其时间复杂度和空间复杂度
- 排序算法-堆排序
- 三种改进型排序算法-快速排序,堆排序,希尔排序
- 排序算法之堆排序
- (高效率排序算法三)堆排序
- 排序算法七:选择排序之堆排序
- 【算法分析】排序算法:希尔、归并、快速、堆排序
- 排序算法总结4-堆排序
- 排序算法----堆排序
- 算法导论学习笔记(一)排序算法之堆排序
- 排序算法之堆排序
- 排序算法——堆排序
- 时间复杂度为O(N*logN)的排序算法——归并排序、快速排序、堆排序