排序算法 | 堆排序,算法的图解、实现、复杂度和稳定性分析
- 今天講解一下堆排序的原理以及實現、復雜度和穩定性分析
目錄
- 1 堆的定義
- 2 堆排序的思路
- 3 代碼實現
- 4 堆的輸出(刪除操作)
- 5 堆的插入操作
- 6 堆排序的特點
- 7 性能分析
1 堆的定義
堆排序(英語:Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆是一個近似完全二叉樹的結構,并同時滿足堆積的性質:即子結點的鍵值或索引總是小于(或者大于)它的父節點。
定義:
n個關鍵字序列L[1…n]稱為堆,當且僅當該序列滿足:
① L(i)>=L(2i) 且 L(i)>=L(2i+1)或
② L(i)<=L(2i) 且 L(i)<=L(2i+1) (1≤isln/2])
可以將該一維數組視為一棵完全二叉樹
大根堆:滿足條件① 的堆稱為大根堆(大頂堆),大根堆的最大元素存放在根結點,且其任一非根結點的值小于等于其雙親結點值
對于堆中的元素編號,其實也是邏輯結構映射到數組的一個過程;
小根堆:滿足條件2的堆稱為小根堆(小頂堆),小根堆的定義剛好相反,根結點是最小元素
2 堆排序的思路
首先將存放在L[1…n]中的n個元素建成初始堆,由于堆本身的特點(以大頂堆為例),堆頂元素就是最大值。
輸出堆頂元素后,通常將堆底元素送入堆頂,堆被破壞,將堆頂元素向下調整使其繼續保持大頂堆的性質,再輸出堆頂元素。如此重復。
- 根據數組構建一個完全二叉樹
- 從最后一個非葉節點開始調整,使得子樹成為堆
- 如此重復,直至滿足堆的定義
值得注意的是:
大根堆排序結果為升序
小根堆排序結果為降序
這是一個常見的誤區!
這是因為:堆使用的時候都是每次把堆頂的元素干掉留下堆內部的元素做成Top N
如果你要找100000中的 TOP100最大的,你用小根堆
如果你要找100000中的 TOP100最小的,你用大根堆
3 代碼實現
void BuildMaxHeap(int *arr,int len) {// 建立初始大根堆 for(int i = (len-1)/2; i >= 0; i--){HeapAdjust(arr,i,len);} }// 子樹調整 void HeapAdjust(int *arr,int k,int len) {int temp = arr[k]; // 暫存根節點 for(int i = k*2+1;i < len;i = i*2+1){if (i < len-1 && arr[i] < arr[i+1])i++;if (temp >= arr[i])break; // 篩選結束 else {arr[k] = arr[i];k = i; // 繼續向下篩選 }}arr[k] = temp ; // 篩選的值放入最終位置 }void HeapSort(int *arr,int len) {BuildMaxHeap(arr,len);for(int i = len-1;i > 0;i--){ arr[0] = arr[0] ^ arr[i]; arr[i] = arr[0] ^ arr[i]; arr[0] = arr[0] ^ arr[i];HeapAdjust(arr,0,i);} }4 堆的輸出(刪除操作)
堆中根結點的值肯定是最值,不是最大就是最小,往往需要使用到。
即每次都刪除第0個數據(根結點)
那么堆中輸出根節點之后如何保證堆原有的特性呢?
刪除之后,打破了原有規律,需要調整!
-
刪除根結點
-
將最后一個數據的值賦給根結點
-
然后再從根結點開始進行一次從上向下的調整
5 堆的插入操作
-
對堆進行插入操作時,先將新結點放在堆的末端
-
隨后再向上執行調整操作
6 堆排序的特點
堆排序適合關鍵字較多的情況(如n>1000)
不適合關鍵字較少的情況
大根堆排序結果為升序
小根堆排序結果為降序
例如,在1千萬個數中選出前100個最大值?
首先使用一個大小為100的數組,讀入前100個數,建立小頂堆,而后依次讀入余下的數,若小于堆頂則舍棄,否則用該數取代堆頂并重新調整堆,待數據讀取完畢,堆中100個數即為所求。
7 性能分析
【空間復雜度】:僅使用常數個輔助單元,空間復雜度為 O(1)
【時間復雜度】:建堆時間為 O(n),之后有n-1次向下調整操作,每次調整的時間復雜度為 O(h) h表示樹高
故在最好、最壞和平均情況下,堆排序的時間復雜度為 O(nlog2n)
【穩定性】:進行篩選時,有可能把后面相同關鍵字的元素調整到前面,所以堆排序算法是一種不穩定的排序方法
總結
以上是生活随笔為你收集整理的排序算法 | 堆排序,算法的图解、实现、复杂度和稳定性分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 排序算法 | 简单选择排序,算法的图解、
- 下一篇: java字符串操作