堆積排序(Heap Sort)演算法,利用完全二元樹來排序的演算法

文章推薦指數: 80 %
投票人數:10人

堆積排序(Heap Sort)演算法是利用完全二元樹(Complete Binary Tree),也就是堆積(Heap)結構來完成排序的演算法。

雖然說要用到堆積結構,看起來好像很 ... MagicLen 內容 堆積排序(HeapSort)演算法,利用完全二元樹來排序的演算法 2019年4月5日 MagicLen 研究分享、Go、Java、NodeJS、Rust、演算法 編輯 本篇文章更新於 2022年6月7日20時 堆積排序(HeapSort)演算法是利用完全二元樹(CompleteBinaryTree),也就是堆積(Heap)結構來完成排序的演算法。

雖然說要用到堆積結構,看起來好像很複雜似的,但其實這個只要一般的陣列結構(可以直接用要排序的陣列來製作)就能實作出來,而且實作出來的速度保證不會太慢,再怎麼差都會有O(nlogn)的時間複雜度。

堆積排序法(HeapSort) 堆積排序法的概念 堆積排序有法兩個大步驟,第一個是把要排序的陣列製作成「最小堆積」(MinHeap)或是「最大堆積」(MaxHeap)。

如果要將陣列遞增排序的話就使用最大堆積;如果要遞減排序的話就使用最小堆積。

接下來的步驟就是利用最大堆積和最小堆積來進行排序,方法和建立最大堆積或是最小堆積時是幾乎一樣的。

什麼是完全二元樹?完全二元樹是一種二元樹,它只允許最後一層(最底下那層)的節點數量可以不必填滿(若頂層是第1層,則第n層的最大節點數量為2n-1)。

例如以下結構即為完全二元樹: 5 /\ 13 /\/ 624 什麼是最小堆積?最小堆積就是上層節點數值必定會小於下層節點數值的完全二元樹。

例如以下結構即為最小堆積: 1 /\ 25 /\/ 463 什麼是最大堆積?最大堆積就是上層節點數值必定會大於下層節點數值的完全二元樹。

例如以下結構即為最大堆積: 6 /\ 53 /\/ 421 陣列可以依照索引順序,由左至右、由上到下表示出一個完全二元樹。

例如以上的最大堆積,可以是以下這個陣列: 索引012345 數值653421 堆積排序法的過程 假設現在有個陣列資料,內容如下: 索引0123456789 數值698130389247613279 要如何將它遞增排列呢? 我們先將這個陣列用完全二元樹的結構來表示,如下: 69 /\ /\ /\ 8130 /\/\ /\/\ 389247 /\/ 613279 由於我們要進行遞增,因此要先將這個完全二元樹變成最大堆積。

於是我們從最下面的兩層開始,把比上層大的元素交換到上層。

首先看到47,由於它沒有子節點,因此還不用交換。

69 /\ /\ /\ 8130 /\/\ /\/\ 389247* /\/ 613279 2,也沒有子節點,因此不用交換。

69 /\ /\ /\ 8130 /\/\ /\/\ 3892*47 /\/ 613279 再來看到9,9有一個值為79的子節點。

由於79比9大,所以要交換上來。

69 /\ /\ /\ 8130 /\/\ /\/\ 389*247 /\/ 613279↑ 69 /\ /\ /\ 8130 /\/\ /\/\ 3879+247 /\/ 61329* 此時9已沒有子節點了,結束交換。

再來看到38,38有兩個子節點,最大的子節點為61。

由於61比38大,所以要交換上來。

69 /\ /\ /\ 8130 /\/\ /\/\ 38*79247 /\/ 61↑329 69 /\ /\ /\ 8130 /\/\ /\/\ 61+79247 /\/ 38*329 此時38已沒有子節點了,結束交換。

依此類推,剩下的最大堆積的建構過程如下: 69 /\ /\ /\ 8130* /\/\ /\/\ 6179247↑ /\/ 38329 69 /\ /\ /\ 8147+ /\/\ /\/\ 6179230* /\/ 38329 69 /\ /\ /\ 81*47 /\/\ /\/\ 6179230 /\/ 38329 69* /\ /\ /\ 81↑47 /\/\ /\/\ 6179230 /\/ 38329 81+ /\ /\ /\ 69*47 /\/\ /\/\ 6179↑230 /\/ 38329 81+ /\ /\ /\ 7947 /\/\ /\/\ 6169*230 /\/ 38329 81 /\ /\ /\ 7947 /\/\ /\/\ 6169230 /\/ 38329 至此,最大堆積就建立好了! 接下來的工作就是排序啦!由於我們現在已經知道這個結構中的最大值是最頂層的節點,且我們要把陣列做遞增排列。

所以我們可以直接把頂層的節點,也就是最大值與陣列的最後一個元素做交換,如此一來我們的遞增排序就已經排好一個元素了。

9* /\ /\ /\ 79↑47 /\/\ /\/\ 6169230 /\/ 383281- 但別忘了要繼續維持住我們的最大堆積條件,所以還是得做交換才行。

由於此時81已經在陣列的正確位置上,我們可以直接把它從最大堆積的考慮範圍中排除,不要再去動它了。

79+ /\ /\ /\ 9*47 /\/\ /\/\ 6169↑230 /\/ 383281- 79+ /\ /\ /\ 6947 /\/\ /\/\ 619*230 /\/ 383281- 79 /\ /\ /\ 6947 /\/\ /\/\ 619230 /\/ 383281- 接下來就是讓32要與這個最大堆積結構中的最大值,也就是最頂層的節點做交換啦!之後的步驟都跟剛才把81換下來時一樣,如下: 32* /\ /\ /\ 69↑47 /\/\ /\/\ 619230 /\/ 3879-81- 69+ /\ /\ /\ 32*47 /\/\ /\/\ 61↑9230 /\/ 3879-81- 69+ /\ /\ /\ 6147 /\/\ /\/\ 32*9230 /\/ 38↑79-81- 69+ /\ /\ /\ 6147 /\/\ /\/\ 389230 /\/ 32*79-81- 69 /\ /\ /\ 6147 /\/\ /\/\ 389230 /\/ 3279-81- 32* /\ /\ /\ 61↑47 /\/\ /\/\ 389230 /\/ 69-79-81- 61+ /\ /\ /\ 32*47 /\/\ /\/\ 38↑9230 /\/ 69-79-81- 61+ /\ /\ /\ 3847 /\/\ /\/\ 32*9230 /\/ 69-79-81- 61 /\ /\ /\ 3847 /\/\ /\/\ 329230 /\/ 69-79-81- 30* /\ /\ /\ 3847↑ /\/\ /\/\ 329261- /\/ 69-79-81- 47+ /\ /\ /\ 3830* /\/\ /\/\ 329261- /\/ 69-79-81- 47 /\ /\ /\ 3830 /\/\ /\/\ 329261- /\/ 69-79-81- 2* /\ /\ /\ 38↑30 /\/\ /\/\ 32947-61- /\/ 69-79-81- 38+ /\ /\ /\ 2*30 /\/\ /\/\ 32↑947-61- /\/ 69-79-81- 38+ /\ /\ /\ 3230 /\/\ /\/\ 2*947-61- /\/ 69-79-81- 38 /\ /\ /\ 3230 /\/\ /\/\ 2947-61- /\/ 69-79-81- 9* /\ /\ /\ 32↑30 /\/\ /\/\ 238-47-61- /\/ 69-79-81- 32+ /\ /\ /\ 9*30 /\/\ /\/\ 238-47-61- /\/ 69-79-81- 32 /\ /\ /\ 930 /\/\ /\/\ 238-47-61- /\/ 69-79-81- 2* /\ /\ /\ 930↑ /\/\ /\/\ 32-38-47-61- /\/ 69-79-81- 30+ /\ /\ /\ 92* /\/\ /\/\ 32-38-47-61- /\/ 69-79-81- 30 /\ /\ /\ 92 /\/\ /\/\ 32-38-47-61- /\/ 69-79-81- 2* /\ /\ /\ 9↑30- /\/\ /\/\ 32-38-47-61- /\/ 69-79-81- 9+ /\ /\ /\ 2*30- /\/\ /\/\ 32-38-47-61- /\/ 69-79-81- 9 /\ /\ /\ 230- /\/\ /\/\ 32-38-47-61- /\/ 69-79-81- 2 /\ /\ /\ 9-30- /\/\ /\/\ 32-38-47-61- /\/ 69-79-81- 這樣就完成排序啦! 堆積排序法的程式實作 Rust Java Node.js Go ///堆積排序法。

fnheap_sort_inner(array:&mut[i32],shift_down:&dynFn(&mut[i32],usize,usize)){ letlength=array.len(); letlength_dec=length-1; forstartin(0..(length>>1)).rev(){ shift_down(array,start,length_dec); } foriin(1..length).rev(){ array.swap(0,i); shift_down(array,0,i-1); } } #[inline] fnheap_sort_shift_down(array:&mut[i32],mutstart:usize,end:usize){ loop{ letleft=(start<<1)+1; ifleft>end{ break; } letright=left+1; letmax_child=ifright<=end&&array[right]>array[left]{ right }else{ left }; ifarray[start]>=array[max_child]{ break; } array.swap(start,max_child); start=max_child; } } #[inline] fnheap_sort_desc_shift_down(array:&mut[i32],mutstart:usize,end:usize){ loop{ letleft=(start<<1)+1; ifleft>end{ break; } letright=left+1; letmin_child=ifright<=end&&array[right]>1)-1;start>=0;--start){ shiftDown.shiftDown(array,start,lengthDec); } for(inti=lengthDec;i>=0;--i){ finalinttemp=array[0]; array[0]=array[i]; array[i]=temp; shiftDown.shiftDown(array,0,i-1); } } staticclassAscShiftDownextendsShiftDown{ @Override voidshiftDown(finalint[]array,intstart,finalintend){ while(true){ finalintleft=(start<<1)+1; if(left>end){ break; } finalintright=left+1; finalintmaxChild=(right<=end&&array[right]>array[left])?right:left; if(array[start]>=array[maxChild]){ break; } finalinttemp=array[start]; array[start]=array[maxChild]; array[maxChild]=temp; start=maxChild; } } } staticclassDescShiftDownextendsShiftDown{ @Override voidshiftDown(finalint[]array,intstart,finalintend){ while(true){ finalintleft=(start<<1)+1; if(left>end){ break; } finalintright=left+1; finalintminChild=(right<=end&&array[right]>1)-1;start>=0;start--){ shiftDown(array,start,lengthDec); } for(leti=lengthDec;i>=1;i--){ consttemp=array[0]; array[0]=array[i]; array[i]=temp; shiftDown(array,0,i-1); } } functionheapSortShiftDown(array,start,end){ for(;;){ constleft=(start<<1)+1; if(left>end){ break; } constright=left+1; constmaxChild=(right<=end&&array[right]>array[left])?right:left; if(array[start]>=array[maxChild]){ break; } consttemp=array[start]; array[start]=array[maxChild]; array[maxChild]=temp; start=maxChild; } } functionheapSortDescShiftDown(array,start,end){ for(;;){ constleft=(start<<1)+1; if(left>end){ break; } constright=left+1; constminChild=(right<=end&&array[right]>1)-1;start>=0;start-=1{ shiftDown(array,start,lengthDec) } fori:=lengthDec;i>=1;i-=1{ array[0],array[i]=array[i],array[0] shiftDown(array,0,i-1) } } funcheapSortShiftDown(array[]int,startint,endint){ for{ left:=(start<<1)+1 ifleft>end{ break } right:=left+1 varmaxChildint ifright<=end&&array[right]>array[left]{ maxChild=right }else{ maxChild=left } ifarray[start]>=array[maxChild]{ break } array[start],array[maxChild]=array[maxChild],array[start] start=maxChild } } funcheapSortDescShiftDown(array[]int,startint,endint){ for{ left:=(start<<1)+1 ifleft>end{ break } right:=left+1 varminChildint ifright<=end&&array[right]>1等同於「除以2取整數」;<<1等同於「乘以2」。

堆積排序法的複雜度 以#{{n}}#來表示要排序的資料筆數。

項目 值 備註 最差時間複雜度 #{{O(n\logn)}}# 最佳時間複雜度 #{{O(n)}}# 要排序的元素值都一樣時。

平均時間複雜度 #{{O(n\logn)}}# 最差空間複雜度 #{{O(1)}}# 是否穩定 否 SortingAlgorithm、排序演算法、堆積排序、HeapSort、完全二元樹、CompleteBinaryTree、最小堆積、MinHeap、MaxHeap、最大堆積 關於作者 MagicLen 各位好,我是MagicLen,是這網站的管理員。

我是台灣台中大肚山上人,畢業於台中高工資訊科和台灣科技大學資訊工程系,曾在桃機航警局服役。

我熱愛自然也熱愛科學,喜歡和別人分享自己的知識與經驗。

如果你有興趣認識我,可以加我的Facebook(點我),並且請註明是從MagicLen來的。

載入中…… 隨機文章 Rust程式語言如何將定數於編譯階段時串接成字串? 2020年9月3日 淺談媒介近用權─CH3有線電視公用頻道 2014年8月13日 Rust學習之路─第九章:錯誤處理 2018年6月19日 台北大縱走第一段:關渡─光武山─忠義山(嘎嘮別山)─向天池─向天山─面天山─二子坪 2020年7月24日 Rust程式語言可以單檔執行!如何把外部檔案和Rust程式編譯在一起? 2018年10月26日 免費訂閱本站電子報 交換連結 哈啦客談天說地 港澳資訊 電腦綠生活PCGreenLife 阿摩斯的小確幸 半熟態度-歐美加 英文練習網 低調一點 迴旋人生 iZO手札 冠均網頁設計公司 Bon部落網 坂本Sakamoto.blog-探究科技未知領域 FuYUan'sSpace Mayday麥帶先生 程式的奇技淫巧之道 申請交換連結 申請交換連結 ×



請為這篇文章評分?