堆排序 一、什么是堆排序?
基于堆实现的排序算法
利用堆顶的有序性(最大值或最小值)来实现对数组的排序
二、为什么要用堆排序?
唯一能够同时最优地利用空间和时间的排序算法
缺点是很少和相邻元素比较,导致无法利用缓存
三、怎么实现堆排序? 堆排序可以分为 2 步:
构建堆:将数组构建成堆
堆排序:一直移除堆顶元素,直到堆为空为止
先将数据建成二叉树的形式,再进行排序。
3.1 构建堆 因为每次都是移除堆顶,而堆顶一直都是最大值/最小值,所以移除堆元素就是排序的过程。
比如说,数组 [2, 7, 1, 4, 3, 6, 5]
,要从小到大排序。
第一步是构建堆,将数组构建出大顶堆的结构:
1 2 3 4 5 6 7 1=>6 7 2=>7=>4 2 2 2 7 / \ / \ / \ / \ 7 1 7 6 7 6 4 6 / \ / \ / \ / \ / \ / \ / \ / \ 4 3 6 5 4 3 1 5 4 3 1 5 2 3 1 5
构建完堆以后,数组变成了这样:
1 2 3 4 5 6 7 8 7 / \ 4 6 -- 逻辑结构(二叉树) / \ / \ 2 3 1 5 ___ ___ ___ ___ ___ ___ ___ | 7 | 4 | 6 | 2 | 3 | 1 | 5 | -- 物理结构(数组)
3.2 堆排序 接下来是排序,排序过程就是一直移除堆顶元素。
被移除的堆顶元素放到数组的末尾空位(相当于末尾已经不算在堆内了):
1 2 3 4 5 6 7 7 6 / \ / \ 4 6 => 4 5 / \ / \ / \ / 2 3 1 5 2 3 1 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ | 7 | 4 | 6 | 2 | 3 | 1 | 5 | | 6 | 4 | 5 | 2 | 3 | 1 | | 7 |
注意,移除堆顶的同时会调整堆结构(这个是堆的性质)。
以此类推,一直移除堆顶元素,直到堆变成空为止。
移除堆顶 6:
1 2 3 4 5 6 7 6 5 / \ / \ 4 5 => 4 1 / \ / / \ 2 3 1 2 3 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ | 6 | 4 | 5 | 2 | 3 | 1 | | 7 | | 5 | 4 | 1 | 2 | 3 | | 6 | 7 |
移除堆顶 5:
1 2 3 4 5 6 7 5 4 / \ / \ 4 1 => 3 1 / \ / 2 3 2 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ | 5 | 4 | 1 | 2 | 3 | | 6 | 7 | | 4 | 3 | 1 | 2 | | 5 | 6 | 7 |
移除堆顶 4:
1 2 3 4 5 6 7 4 3 / \ / \ 3 1 => 2 1 / 2 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ | 4 | 3 | 1 | 2 | | 3 | 6 | 7 | | 3 | 2 | 1 | | 4 | 5 | 6 | 7 |
移除堆顶 3:
1 2 3 4 5 3 2 / \ => \ 2 1 1 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ | 3 | 2 | 1 | | 4 | 5 | 6 | 7 | | 2 | 1 | | 3 | 4 | 5 | 6 | 7 |
移除堆顶 2:
1 2 3 4 5 2 => 1 \ 1 ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ | 2 | 1 | | 3 | 4 | 5 | 6 | 7 | | 1 | | 2 | 3 | 4 | 5 | 6 | 7 |
至此,堆排序结束,数据已经排好序了。
四、算法分析 4.1 时间复杂度
最好时间复杂度:O(n)
最坏时间复杂度:O(nlogn)
平均时间复杂度:O(nlogn)
4.2 空间复杂度
4.3 稳定性
总结
基于堆实现的排序算法
利用堆顶的有序性(最大值或最小值)来实现对数组的排序
优缺点
优点:唯一能够同时最优地利用空间和时间的排序算法
缺点:很少和相邻元素比较,导致无法利用缓存
堆排序步骤
构建堆:将数组构建成堆
堆排序:一直移除堆顶元素,直到堆为空为止
参考
《算法(第4版)》
https://juejin.cn/post/7119807721766912030
附录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class HeapSort implements Sort { @Override public void sort (int [] arr) { int n = arr.length; for (int i = (n - 1 ) / 2 ; i >= 0 ; i--) { sink(arr, i, n); } for (int i = n; i > 0 ; i--) { swap(arr, 0 , i - 1 ); sink(arr, 0 , i - 1 ); } } private void sink (int [] arr, int i, int n) { while (i < n - 1 ) { int m = i; int l = 2 * i + 1 ; if (l < n && arr[l] > arr[m]) { m = l; } int r = 2 * i + 2 ; if (r < n && arr[r] > arr[m]) { m = r; } if (m == i) { break ; } swap(arr, i, m); i = m; } } }