堆的定義
堆就是一棵可以自我平衡的完全二叉樹
優先隊列的底層數據結構就是堆,實現和堆基本一樣
由於堆存儲在下標從0開始計數的數組中,因此,在堆中給定下標為i的結點時:
如果 i = 0,結點 i 是根結點,無父結點;否則結點 i 的父結點為結點 [(i - 2) / 2]
如果 2i + 1 > n - 1,則結點 i 無左子女;否則結點 i 的左子女為結點 2i + 1
如果 2i + 2 > n - 1,則結點 i 無右結點;否則結點 i 的右子女為結點 2i + 2
堆特性
1、每個父節點都大於等於其所有后代結點。
2、堆的根節點含有堆中最大(或者最小)的對象。
3、堆中以任意節點為根節點的子樹仍然是堆。
堆的分類
最大堆(大根堆,大頂堆):根節點對象是最大對象的堆
最小堆(小根堆,小頂堆):根節點對象是最小對象的堆
堆的插入操作(以最小堆為例,將一個數組變成堆)
堆的插入操作是自底向上進行的,每次從堆的最后一個結點開始插入(將插入的值放入完全二叉樹的最后一個結點),為了維持堆的性質,還要從插入結點開始依次往前遞歸,去維持堆的三個特性
①我們現在有一個長度為n的數組a,里邊的元素都沒有順序,把這個數組變成最小堆
②然后我們新建一個完全二叉樹b,按數組下標0到n-1的順序依次放入完全二叉樹,也就是說現在b[0]=a[0],b[1]=a[1]......。
③取插入結點的父節點,比較父節點的值和插入結點的值,將較小的值交換到父節點位置。
④再以父節點為當前結點,重復上一步操作,知道遇到父節點比插入結點值小,就可以結束遞歸0
//建堆時間復雜度O(n)
int Heap[MAX_SIZE]; int Cur = 0; void Insert(int val) //插入 { Heap[++Cur] = val; //新元素加入堆 int Temp_Cur = Cur; //加入元素后維持堆的性質,一直遞歸到符合堆性質的結點 while (Temp_Cur > 1) { //找父節點 int Root = Temp_Cur / 2; //父節點比子節點大-->小根堆 if (Heap[Root] > val) swap(Heap[Root], Heap[Temp_Cur]); //交換 //找到符合性質的堆就退出 else break; //更新當前結點位置 Temp_Cur = Root; } }
堆的刪除操作
堆的刪除操作是只能刪除堆頂的元素,刪除堆頂元素之后,把堆的最后一個元素放到堆頂,然后不斷向下維護堆的特性1、2、3
//刪除堆頂,接着維護堆的性質的時間復雜度為O(lgn)
int Pop() //刪除 { if (Cur == 0) //堆空 return -99999999; //保存堆頂 int Temp_Top = Heap[1]; //將末尾結點提到樹根 Heap[1] = Heap[Cur]; int Root = 1; //從堆頂往下維護堆的特性,一直處理到葉子結點-->小根堆 while (2 * Root < Cur) { //左兒子 int L_Child = Root * 2; //右兒子 int R_Child = Root * 2 + 1; //只有左節點或者左節點比右節點的值小 if (R_Child >= Cur || Heap[L_Child] < Heap[R_Child]) { //讓左結點和根節點比較,把較小值交換到父節點位置上 if (Heap[Root] > Heap[L_Child]) { swap(Heap[Root], Heap[L_Child]); //更新位置 Root = L_Child; } else break; } //左結點值比右結點值大,比較父節點和右節點 else { if (Heap[Root] > Heap[R_Child]) { swap(Heap[Root], Heap[R_Child]); Root = R_Child; } else break; } } Cur--; //返回刪除的堆頂元素 return Temp_Top; }
堆的排序操作
//堆排序
//排序的空間復雜度為O(1)
int temp[MAX_SIZE]; int k = 0; void quick_sort(int n) { for (int i = 1; i <= n; i++) { temp[k++] = Pop(); } }
完整代碼
#include<iostream> #include<string> #define MAX_SIZE 100005 using namespace std; int Heap[MAX_SIZE]; int Cur = 0; void Insert(int val) //插入 { Heap[++Cur] = val; //新元素加入堆 int Temp_Cur = Cur; //加入元素后維持堆的性質,一直遞歸到符合堆性質的結點 while (Temp_Cur > 1) { //找父節點 int Root = Temp_Cur / 2; //父節點比子節點大-->小根堆 if (Heap[Root] > val) swap(Heap[Root], Heap[Temp_Cur]); //交換 //找到符合性質的堆就退出 else break; //更新當前結點位置 Temp_Cur = Root; } } int Pop() //刪除 { if (Cur == 0) //堆空 return -99999999; //保存堆頂 int Temp_Top = Heap[1]; //將末尾結點提到樹根 Heap[1] = Heap[Cur]; int Root = 1; //從堆頂往下維護堆的特性,一直處理到葉子結點-->小根堆 while (2 * Root < Cur) { //左兒子 int L_Child = Root * 2; //右兒子 int R_Child = Root * 2 + 1; //只有左節點或者左節點比右節點的值小 if (R_Child >= Cur || Heap[L_Child] < Heap[R_Child]) { //讓左結點和根節點比較,把較小值交換到父節點位置上 if (Heap[Root] > Heap[L_Child]) { swap(Heap[Root], Heap[L_Child]); //更新位置 Root = L_Child; } else break; } //左結點值比右結點值大,比較父節點和右節點 else { if (Heap[Root] > Heap[R_Child]) { swap(Heap[Root], Heap[R_Child]); Root = R_Child; } else break; } } Cur--; //返回刪除的堆頂元素 return Temp_Top; } //堆排序 int temp[MAX_SIZE]; int k = 0; void quick_sort(int n) { for (int i = 1; i <= n; i++) { temp[k++] = Pop(); } } int main() { int n, x; cin >> n; for (int i = 0; i < n; i++) { cin >> x; Insert(x); } quick_sort(n); for (int i = 0; i <k; i++) { cout << temp[i] << ' '; } cout << endl; system("pause"); return 0; }