Java常見排序演算法詳解——堆排序
轉載請註明出處: https://www.jianshu.com/p/73ef54fb99f4
概念
二叉樹
要了解堆首先得了解一下二叉樹,在電腦科學中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。二叉樹常被用於實現二叉查詢樹和二叉堆。
二叉樹的每個結點至多隻有二棵子樹(不存在度大於 2 的結點),二叉樹的子樹有左右之分,次序不能顛倒。二叉樹的第 i 層至多有 2i - 1 個結點;深度為 k 的二叉樹至多有 2k - 1 個結點;對任何一棵二叉樹 T,如果其終端結點數為 n0,度為 2 的結點數為 n2,則n0 = n2 + 1。
樹和二叉樹的三個主要差別:
樹的結點個數至少為 1,而二叉樹的結點個數可以為 0
樹中結點的最大度數沒有限制,而二叉樹結點的最大度數為 2
樹的結點無左、右之分,而二叉樹的結點有左、右之分
二叉樹又分為完全二叉樹(complete binary tree)和滿二叉樹(full binary tree)
如圖:

滿二叉樹:一棵深度為 k,且有 2k - 1 個節點稱之為滿二叉樹
完全二叉樹:深度為 k,有 n 個節點的二叉樹,當且僅當其每一個節點都與深度為 k 的滿二叉樹中序號為 1 至 n 的節點對應時,稱之為完全二叉樹

堆
堆(二叉堆)可以視為一棵完全的二叉樹,完全二叉樹的一個“優秀”的性質是,除了最底層之外,每一層都是滿的,這使得堆可以利用陣列來表示(普通的一般的二叉樹通常用連結串列作為基本容器表示),每一個結點對應陣列中的一個元素。
如下圖,是一個堆和陣列的相互關係

二叉堆一般分為兩種:最大堆和最小堆。
最大堆:
最大堆中的最大元素值出現在根結點(堆頂)
堆中每個父節點的元素值都大於等於其孩子結點(如果存在)

最小堆:
最小堆中的最小元素值出現在根結點(堆頂)
堆中每個父節點的元素值都小於等於其孩子結點(如果存在)

原理
- 最大堆調整(Max_Heapify):從堆的倒數第一個非葉子節點作調整,使得子節點永遠小於父節點。沒有必要從葉子節點開始,葉子節點可以看作是已符合堆特點的節點。
- 建立最大堆(Build_Max_Heap):將堆所有資料重新排序
- 堆排序(HeapSort):移除位在第一個資料的根節點,並做最大堆調整。
圖解:列如我們有原始數字[2 10 9 5 6 1]
下面我們用堆排序排序
原始為:
第一次:

第二次

我們得到了

程式碼實現:
/** * 堆排序的主要入口方法,共兩步。 */ public void sort() { /* *第一步:將陣列堆化 *beginIndex = 第一個非葉子節點。 *從第一個非葉子節點開始即可。無需從最後一個葉子節點開始。 *葉子節點可以看作已符合堆要求的節點,根節點就是它自己且自己以下值為最大。 */ int len = array.length - 1; int beginIndex = (len - 1) >> 1; for (int i = beginIndex; i >= 0; i--) maxHeapify(i, len); /* * 第二步:對堆化資料排序 * 每次都是移出最頂層的根節點A[0],與最尾部節點位置調換,同時遍歷長度 - 1。 * 然後從新整理被換到根節點的末尾元素,使其符合堆的特性。 * 直至未排序的堆長度為 0。 */ for (int i = len; i > 0; i--) { swap(0, i); maxHeapify(0, i - 1); } } private void swap(int i, int j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } /** * 調整索引為 index 處的資料,使其符合堆的特性。 * * @param index 需要堆化處理的資料的索引 * @param len 未排序的堆(陣列)的長度 */ private void maxHeapify(int index, int len) { int li = (index << 1) + 1; // 左子節點索引 int ri = li + 1;// 右子節點索引 int cMax = li;// 子節點值最大索引,預設左子節點。 if (li > len) return;// 左子節點索引超出計算範圍,直接返回。 if (ri <= len && array[ri] > array[li]) // 先判斷左右子節點,哪個較大。 cMax = ri; if (array[cMax] > array[index]) {//若“<”這是從大到小 swap(cMax, index);// 如果父節點被子節點調換, maxHeapify(cMax, len);// 則需要繼續判斷換下後的父節點是否符合堆的特性。 } }
算法系列:
完整程式碼:
Java和Kotlin程式碼我均放在了GitHub上,歡迎Star!
GitHub地址: https://github.com/yang0range/MyAlgorithm
歡迎關注公共號
關注公共號會有更多收穫!
