隨便說說堆(一)——二叉堆
阿新 • • 發佈:2018-12-22
復雜 ges union 最大 輸入 個數 img image 下標
二叉堆可以被看作是一個數組,也可以簡單的看作是一個近似的完全二叉樹,二叉堆有最大堆和最小堆,分別具有堆的性質:最大堆的某個結點的值最多與其父結點一樣大,最小堆則是某個結點的值最多與其父結點一樣小。所以最大堆中最大的結點永遠是根結點,最小堆中最小的結點永遠是根節點。
既然二叉堆是一種數據結構,就有其支持的操作(這裏以最小堆為例):
- make_heap:建立一個空堆,或者把數組中元素轉換成二叉堆。
- insert:插入元素。
- minimun:返回一個最小數。
- extract_min:移除最小結點。
- union:合並堆
make_heap
先給一組數{27,17,3,16,13,10,1,5,7}輸入數組,數組下標從0開始。然後我們畫出這棵樹:
然後根據近似滿二叉樹的性質,有n個結點,[n/2]+1,[n/2]+2,……,n都是葉結點。然後可以通過對除了葉結點以外的結點i(0到n/2)進行一次下濾的操作,若兒子結點有小於結點i的結點,將兒子結點中最小者與結點i交換,再與交換後的位置的兒子結點繼續比較,直到小於兒子結點或者為葉結點為止。遍歷完,就得到最小堆。 時間復雜度建立空堆O(1),立地建堆O(n)。
圖示
代碼實現
1 //維護堆 2 void max_heapify(int *a, int i) { 3 int l = 2 * i + 1; 4 int r = 2* i + 2; 5 int largest; 6 if (a[l] != -1 && a[l] < a[i]) largest = l; 7 else largest = i; 8 if (a[r] != -1 && a[r] < a[largest]) largest = r; 9 if (largest != i) { 10 swap(a[i], a[largest]); 11 max_heapify(a, largest); 12 }13 } 14 //建堆 15 void make_heap(int *a, int n) { 16 for (int i = n / 2; i >= 0; i--) max_heapify(a, i); 17 }
insert
對於插入操作,首先將元素push到數組尾部,然後將其進行上濾操作,也就是將其與父結點比較,如果小於父結點就交換,直到大於等於父結點或者到達根。時間復雜度為O(logn)
舉個例子:在{1,5,3,7,13,10,27,16,17}中插入4。
圖示
代碼實現
1 //n是數組長度 2 void insert(int *a, int num, int &n) { 3 a[n++] = num; 4 for (int i = n - 1; i >= 0; i = (i - 1) / 2) { 5 if (a[i] < a[(i - 1) / 2]) swap(a[i], a[(i - 1) / 2]); 6 else break; 7 } 8 }
minimun
返回最小數,對於最小堆來說返回根即可。時間復雜度O(1)
代碼實現
1 int minimun(int *a) { 2 return a[0]; 3 }
extract_min
移除最小頂點,也就是最小堆的根,此時需要將堆最後一個葉節點摘下替換掉根,這樣不會破壞近似滿二叉樹的結構,然後從上至下更新堆,維護堆的性質。時間復雜度O(logn)
圖示
代碼實現
1 void extract_min(int *a, int &n) { 2 a[0] = a[n - 1]; 3 n--; 4 max_heapify(a, 0); 5 }
union
合並堆的話其實就相當於把兩個堆數組合並,然後重新建堆。所以時間復雜度也是線性的,為O(n)。
代碼實現
1 void Union(int *a, int *b, int &n, int &m) { 2 for (int i = 0; i < m; i++) { 3 a[n++] = b[i]; 4 } 5 m = 0; 6 memset(b, -1, sizeof(b)); 7 for (int i = n / 2; i >= 0; i--) max_heapify(a, i); 8 }
這裏就說完了二叉堆,下一篇在隨便說說堆中的二項堆。
隨便說說堆(一)——二叉堆