【算法知识】详解堆排序算法
點擊藍色字關注我們!
什么是堆
「堆」首先是一個完全二叉樹,「堆」分為「大頂堆」和「小頂堆」;
「大頂堆」 :
每個節點的值大于或等于其左右孩子節點的值,稱為大頂堆。
「小頂堆」同理就是每個節點的值小于或等于其左右孩子節點的值。
「注意」:
每個節點的左右孩子節點的大小關系并沒有限定。
大頂堆舉例
如圖:
大頂堆舉例首先其為一個完全二叉樹,且其每個節點的值都大于或者等于其左右孩子節點的值。
完全二叉樹從上到下,從左到右依次編號,就可以將其進行順序存儲,我們從根節點開始,從0開始編號,存入數組如下:
堆特點
由大頂堆定義知道,如果我們從上到下,從左到右,根節點開始從0編號進行順序存儲的話,并將數組記為arr;
我們可以得到如下式子:
arr[i] >= arr[ 2i + 1] ?&& arr[ i ] >= arr[ 2i + 2];
其中 2i + 1為第 i 個節點的左孩子節點的編號。2i + 2為第 i 個節點的右孩子節點的編號;
同理得小頂堆的特點:
arr[i] <= arr[ 2i + 1] ?&& arr[ i ] <= arr[ 2i + 2];
堆排序基本思想
本文以大頂堆為例,進行講解。
算法步驟如下:
1、首先將待排序序列構建成一個大頂堆(存入數組中),那么這時,整個序列的最大值就是堆頂的根節點;
2、將堆頂元素與最后一個元素交換,那么末尾元素就存入了最大值;
3、將剩余的 n - 1個元素重新構建成一個大頂堆,重復上面的操作;
反復執行,就能得到一個有序序列了。
舉例
給定一個待排序序列數組 arr = [ 0 , 2, ?4, ?1 , 5 ];
先構建成一個完全二叉樹如下;
構建堆
「我們從最后一個非葉子節點開始,從左至右,從下到上,開始調整」;
最后一個非葉子節點的索引即 arr.length / 2向下取整 - 1 ,對于此例就是 5 / 2向下取整 - 1 = 2 - 1 = 1;
即值為2的節點;
我們用左右孩子節點的最大值與該節點進行比較;
此時我們發現它的左右孩子節點的最大值為5,大于2,進行交換;
然后處理下一個非葉子節點,即剛才的索引減去1;1 - 1 = 0;
即:
左右孩子節點為5和4,5最大,且大于該節點的值,發生交換;
構建堆4這時我們發現了一個問題:
「值為0的節點的左右節點又比該節點大了,又不滿足大頂堆的定義了」
繼續進行調整:
構建堆5對非葉子節點調整完畢,構建大頂堆完成。
交換
將堆頂元素與末尾元素進行交換,使得末尾元素最大。
堆頂元素與末尾元素交換當交換完畢后最大的元素已經到達數組末尾;
第一次交換后對數組中其他元素進行排序即可。
剩下的四個元素進行調整進行交換:
第二大元素歸位剩下的元素調整并交換后:
第三大元素歸位剩下的元素調整并交換后:
第三大元素歸位第四大元素歸位置此時也意味著排序完成了。
代碼
先說下調整的代碼;
我們需要三個參數,待排序的數組,數組的長度,還有一個就是調整的哪一個非葉子節點;
再說下堆排序代碼,看好注釋;
//堆排序方法public?static?void?heapSort(int?arr[]){//進行第一次調整for(int?i=arr.length/2?-?1;i>=0;i--){adjustHeap(arr,i,arr.length);}for(int?j=arr.length?-?1;j>0;j--){//進行交換int?temp?=?arr[j];arr[j]?=?arr[0];arr[0]?=?temp;//調整長度為j的那些//這里為什么填0呢//因為我們第一次調整的時候從左到右,從下到上調整的;//交換時只是變動了堆頂元素和末尾元素//末尾元素我們不用去管,因為已經是之前長度最大的了//只需要把當前堆頂元素找到合適的位置即可adjustHeap(arr,0,j);}}完整代碼
import?java.util.Arrays;public?class?Solution?{public?static?void?main(String[]?args)?{int?[]?arr?=?new?int[]{0?,?2,??4,??1?,?5};heapSort(arr);System.out.println(Arrays.toString(arr));}//堆排序方法public?static?void?heapSort(int?arr[]){//進行第一次調整for(int?i=arr.length/2?-?1;i>=0;i--){adjustHeap(arr,i,arr.length);}for(int?j=arr.length?-?1;j>0;j--){//進行交換int?temp?=?arr[j];arr[j]?=?arr[0];arr[0]?=?temp;//調整長度為j的那些//這里為什么填0呢//因為我們第一次調整的時候從左到右,從下到上調整的;//交換時只是變動了堆頂元素和末尾元素//末尾元素我們不用去管,因為已經是之前長度最大的了//只需要把當前堆頂元素找到合適的位置即可adjustHeap(arr,0,j);}}/***?author:微信公眾號:code隨筆*?@param?arr?待排序的數組*?@param?i???表示等待調整的哪個非葉子節點的索引*?@param?length?待調整長度*/public?static?void?adjustHeap(int?arr[],int?i,int?length){//非葉子節點的值int?notLeafNodeVal?=?arr[i];//k的初始值為當前非葉子節點的左孩子節點的索引//k?=?2?*?k?+?1表示再往左子節點找for(int?k?=?i?*?2?+?1;k<length;k=2?*k?+?1){//如果k?+?1還在待調整的長度內,且右子樹的值大于等于左子樹的值//將k++,此時為當前節點的右孩子節點的索引if(k+1<length?&&?arr[k]?<?arr[k+1]){k++;}//如果孩子節點大于當前非葉子節點if(arr[k]?>?notLeafNodeVal){arr[i]?=?arr[k];//將當前節點賦值為孩子節點的值i?=?k;//將i賦值為孩子節點的值,再看其孩子節點是否有比它大的}else{break;//能夠break的保證是,我們是從左至右,從下到上進行調整的//只要上面的不大于,下面的必不大于}}//循環結束后,將i索引處的節點賦值為之前存的那個非葉子節點的值arr[i]?=?notLeafNodeVal;} }時間復雜度
在建初始堆時,其復雜度為;
交換操作需 n-1 次;
重建堆的過程中近似為;
堆排序時間復雜度為。
穩定性
堆排序是不穩定的:
比如:10,9,6,9;如圖:
當堆頂元素10和末尾元素交換后,兩個9的相對位置發生改變。
已發布:
【算法知識】詳解冒泡算法
【算法知識】詳解選擇排序算法
【算法知識】詳解插入排序算法
【算法知識】詳解快速排序算法
【算法知識】詳解基數排序算法
往期精彩回顧適合初學者入門人工智能的路線及資料下載機器學習及深度學習筆記等資料打印機器學習在線手冊深度學習筆記專輯AI基礎下載(pdf更新到25集)機器學習的數學基礎專輯本站qq群1003271085,加入微信群請回復“加群”獲取一折本站知識星球優惠券,復制鏈接直接打開:https://t.zsxq.com/yFQV7am喜歡文章,點個在看總結
以上是生活随笔為你收集整理的【算法知识】详解堆排序算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NLP中数据增强的综述,快速的生成大量的
- 下一篇: 过来人讲述:研究生复试之注意事项