視頻筆記:https://www.bilibili.com/video/BV1Eb41147dK?from=search&seid=7630499509795698118
一,大根堆
堆排序要用到 大根堆的數據結構
1,為完全二叉樹:添加新節點的順序是:從上到下,從左到右
2,滿足:父節點 > 子節點
二,heapify:堆調整
1,針對結點 i,將其兩個子節點找出來,此三個結點構成一個最小單位的完全二叉樹(越界的忽略)
2,找到這個最小單位的完全二叉樹 的最大值,並將其交換至父節點的位置
3,遞歸調用,維護交換后 子節點與其子結點被破壞的堆關系,遞歸出口為葉節點
4,結果,構造一個最小單位的完全二叉樹對應的堆
注意邊界的判定:上界:元素個數:n,在求孩子節點時,如果孩子結點的下標大於等於n,則不參與任何操作(指:不參與堆調整)。而且遞歸的遞歸出口是元素下標大於等於n,這是因為堆調整是自頂向下進行調整的,同樣因此,就不用考慮元素下標小於0的情況。
二,build_heap:構造堆
① 用一維數組表示:堆(因為堆的結構與完全二叉樹一樣)
使用數組下標為 0 的位置,則 a[ i ] 的父節點為 a[ (i-1/)2 ],兩個子節點為 a[ i*2+1 ] 和 a[ i*2+2 ]
② 由於 heapify 會遞歸調用維護下面的堆,所以要構造完整的堆,要從下面往上構造:先找到最后一個父結點,再從最后一個父結點開始向上調用 heapify
三,heap_sort:利用堆進行堆排序
由堆可以得到什么呢 ?首先根結點是最大的,第二層的兩個節點是 第二大和第三大的。
於是,利用這點就可以排序了
1,可以想到:既然根結點已經是最大的,那么就把這個數提走,剩下的再構造一個新堆。然后再提走根結點,再構造新的堆,循環往復。
2,但是這樣就會面臨一個問題?拿走根結點之后,根結點的數值由誰取代?
3,由於該結點要滿足:移動該節點到根結點后,要對堆的結構破壞盡可能小,而且堆的大小還要因為結點少了一個進行減1(即 數組表示堆的范圍要減 一)。顯然最后一個結點是最好的選擇了。移動后既不會影響到后面的結點,原來的位置還因為空了可以直接取消掉。
4,關鍵的 一步出現了。我們用最后一個結點替換根結點,此時根結點和第二層的兩個結點構成一個最小單位的最小二叉樹,此時就可以直接調用 heapify函數 ,形成一個新的堆。沒有必要用 build_heap函數,因為此時最大值就在第二層,根結點會因為遞歸維護被替換到最后一層,其他結點也會因為遞歸維護而得到調整。
5,至於拿出來的結點放哪,當然還是放在原來的數組里咯。由於數組前面存的是堆,於是可以從最后面的位置依次往前放,還可以形成升序序列。這也是為什么要用大根堆,因為默認我們想要的是升序序列,如果想要降序序列的話,也可以用小根堆。
四,代碼
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> void swap(int a[], int i, int j) // 交換結點位置 { int t = a[i]; a[i] = a[j]; a[j] = t; } void heapify(int tree[], int n, int i) // 堆調整 { if (i >= n) return; int c1 = 2 * i + 1; int c2 = 2 * i + 2; int max = i; if (c1 < n&&tree[c1] > tree[max]) max = c1; if (c2 < n&&tree[c2] > tree[max]) max = c2; if (max != i) { swap(tree, max, i); // 將最大值移至父節點 heapify(tree, n, max); // 遞歸維護下面的結點 } } void build_heap(int tree[], int n) // 構造堆 { int last_node = n - 1; int parent = (last_node - 1) / 2; // 最后一個父結點 for (int i = parent; i >= 0; i--) // 從最后一個父結點開始向上調用 heapify heapify(tree, n, i); } void heap_sort(int tree[], int n) // 利用堆進行堆排序 { build_heap(tree, n); for (int i = n - 1; i >= 0; i--) // 每次取出根結點后,堆減少一個節點 { swap(tree, i, 0); heapify(tree, i, 0); // 構造一個新堆 } } int main(void) { int tree[] = { 2,5,3,1,10,4 }; int n = 6; heap_sort(tree, n); for (int i = 0; i < n; i++) { printf("%d ", tree[i]); }puts(""); system("pause"); return 0; }
over ! ! 感謝觀看!!
========== ========= ========= ======= ====== ===== ==== === == =
寄揚州韓綽判官 杜牧 唐
青山隱隱水迢迢,秋盡江南草未凋。
二十四橋明月夜,玉人何處教吹簫。