利用徹底二叉樹的結構來維護一組數據,而後進行相關操做,通常的操做進行一次的時間複雜度在 O(1)~O(logn) 之間。ios
- 若設二叉樹的深度爲h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層全部的結點都連續集中在最左邊,這就是徹底二叉樹。咱們知道二叉樹能夠用數組模擬,堆天然也能夠。
- 從圖中能夠看出,元素的父親節點數組下標是自己的1/2(只取整數部分),因此咱們很容易去模擬,也很容易證實其全部操做都爲log級別~~
顧名思義,就是保證根節點是全部數據中最大/小,而且盡力讓小的節點在上方
不過有一點須要注意:堆內的元素並不必定數組下標順序來排序的!!不少的初學者會錯誤的認爲大/小根堆中
下標爲1就是第一大/小,2是第二大/小……數組
咱們剛剛畫的徹底二叉樹中並無任何元素,如今讓咱們加入一組數據吧!
下標從1到9分別加入:{8,5,2,10,3,7,1,4,6}。測試
以下圖所示
spa
- 上浮 shift_up;
- 下沉 shift_down
- 插入 push
- 彈出 pop
- 取頂 top
- 堆排序 heap_sort
從上述未處理過的數據中能夠很容易得出,根節點1元素8絕對不是最小的
可是它的一個子節點3(元素2)比它小,咱們能夠將它放到最高點,直接進行交換。
此外,子節點3的子節點7(元素1)彷佛更適合在根節點
此時,咱們沒法直接和根節點交換的,那麼就是用上浮 shift_up操做來完成。3d
操做過程以下code
從當前結點開始,和它的父親節點比較,如果比父親節點來的小,就交換,而後將當前詢問的節點下標更新爲原父親節點下標;不然退出。
blog
僞代碼以下:排序
Shift_up( i ) { while( i / 2 >= 1) { if( 堆數組名[ i ] < 堆數組名[ i/2 ] ) { swap( 堆數組名[ i ] , 堆數組名[ i/2 ]) ; i = i / 2; } else break; }
上浮操做結束後,節點3(元素8)與其子節點7(元素2)的位置並不正確。
所以,須要節點3下沉內存
小根堆是盡力要讓小的元素在較上方的節點,而下沉與上浮同樣要以交換來不斷操做。
讓當前結點的子節點(若是存在)做比較,哪一個比較小就和它交換,並更新詢問節點的下標爲被交換的子節點下標,不然退出。
ci
僞代碼以下所示
Shift_down( i , n ) //n表示當前有n個節點 { while( i * 2 <= n) { T = i * 2 ; if( T + 1 <= n && 堆數組名[ T + 1 ] < 堆數組名[ T ]) T++; if( 堆數組名[ i ] < 堆數組名[ T ] ) { swap( 堆數組名[ i ] , 堆數組名[ T ] ); i = T; } else break; }
插入操做
如何在插入的時候維護堆?
每次進行數據插入的時候,往堆的最後插入,而後使用上浮操做。
僞代碼以下所示
Push ( x ) { n++; 堆數組名[ n ] = x; Shift_up( n ); }
彈出操做
使用根節點元素和尾節點進行交換,而後使如今的根元素下沉。
僞代碼以下所示
Pop ( x ) { swap( 堆數組名[1] , 堆數組名[ n ] ); n--; Shift_down( 1 ); }
取頂操做
返回節點數組[0]
堆排序
new 新數組,每次取堆頂元素放進去,而後彈掉堆頂
僞代碼以下所示
Heap_sort( a[] ) { k=0; while( size > 0 ) { k++; a[ k ] = top(); pop(); } }
堆排序的時間複雜度是O(nlogn)
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define maxn 100010 //這部分能夠本身定義堆內存多少個元素 using namespace std; struct Heap { int size, queue[maxn]; Heap() { //初始化 size=0; for(int i=0; i<maxn; i++) queue[i]=0; } void shift_up(int i) { //上浮 while(i > 1) { if(queue[i] < queue[i>>1]) { int temp = queue[i]; queue[i] = queue[i>>1]; queue[i>>1] = temp; } i >>= 1; } } void shift_down(int i) { //下沉 while((i<<1) <= size) { int next = i<<1; if(next < size && queue[next+1] < queue[next]) next++; if(queue[i] > queue[next]) { int temp = queue[i]; queue[i] = queue[next]; queue[next] = temp; i = next; } else return ; } } void push(int x) { //加入元素 queue[++size] = x; shift_up(size); } void pop() { //彈出操做 int temp = queue[1]; queue[1] = queue[size]; queue[size] = temp; size--; shift_down(1); } int top() { return queue[1]; } bool empty() { return size; } void heap_sort() { //另外一種堆排方式,因爲難以證實其正確性 //我就沒有在博客裏介紹了,能夠本身測試 int m=size; for(int i = 1; i <= size; i++) { int temp = queue[m]; queue[m] = queue[i]; queue[i] = temp; m--; shift_down(i); } } }; int main() { Heap Q; int n,a,i,j,k; cin>>n; for(i = 1; i <= n; i++) { cin >> a; Q.push(a); //放入堆內 } for(i = 1; i <= n; i++) { cout << Q.top() << " "; //輸出堆頂元素 Q.pop(); //彈出堆頂元素 } return 0; }