堆排序是最重要的排序演算法之一,在平時的開發以及面試中經常會用到。堆的特點是:
1,有一顆完全二叉樹構成,1;
2,可分為最大堆和最小堆。最大堆的意思就是:任何根節點的資料不小於左右孩子節點的資料;反之,最小堆的意思就是任何節點的資料不大於左右孩子節點的資料;
3,堆排序的演算法複雜度為O(NlgN),比冒泡和插入快,究其原因在於堆只維護局部最大或最小。
4,堆的儲存用數組實現,按層儲存,1;
儲存在數組裡:
圖 1
下面分四部分講解堆,分別為:堆的插入,刪除,以及堆排序,最後是堆化的優先隊列。
一,堆的插入
堆的插入只能在最後一個位置插入, 2。只能在黑色圈的地方插入。插入以後,必須進行更新,以保持堆的性質,即根資料最大或是最小。更新的思路:從插入節點到根節點,依次比較更新,直到滿足條件(本列子就是:根資料小於左右孩子)。
大致原理解:
關鍵代碼如下:
注意的是:左孩子和右孩子的順序分別為2i+1,2i+2。結合上面的流程圖,插入的過程為:從下往上依次比較,重新調整堆的結構。
void MinHeapFixup(int* a, int i) { int j, temp; temp = a[i]; j = (i - 1) / 2; //父結點 while (j >= 0 && i != 0) { if (a[j] <= temp) break; a[i] = a[j]; //把較大的子結點往下移動,替換它的子結點 i = j; j = (i - 1) / 2; } a[i] = temp; } void MinHeapAddNumber(int* a, int n, int nNum) { a[n] = nNum; MinHeapFixup(a, n); }
二,堆的刪除
記住只能刪除最頂部的元素,再把最低端的元素放到最頂端,從上往下依次更新整個堆。如下:
void MinHeapFixdown(int a[], int i, int n) { int j, temp; temp = a[i]; j = 2 * i + 1; while (j < n) { if (j + 1 < n && a[j + 1] < a[j]) //如果存在有孩子,比較左右孩子的大小 j++; if (a[j] >= temp) break; a[i] = a[j]; i = j; j = 2 * i + 1; } a[i] = temp; } void MinHeapDeleteNumber(int a[], int n) { std::swap(a[0], a[n - 1]); MinHeapFixdown(a, 0, n - 1); }
總結:刪除,只能刪除頭結點,然後從上往下依次調整堆;插入,只能在末端插入,然後從下往上依次調整堆。三,堆化數組,採用插入的思想從下往上依次調整堆即可。此時調整每一個非葉子節點即可。關鍵代碼如下:
void MakeMinHeap(int* a, int n) { for (int i = n / 2 - 1; i >= 0; i--) MinHeapFixdown(a, i, n); } 測試:結合圖形中的資料,我們示範一遍:
int main(){int a[8]={9,7,5,6,8,4,10};cout<<"原始數組裡的資料為:";for(int i=0;i<7;++i)cout<<a[i]<<" ";cout<<endl<<"堆化後的數組為:"; MakeMinHeap(a, 7);for(int i=0;i<7;++i)cout<<a[i]<<" "; cout<<endl<<"增加一個資料3後的數組為:"; MinHeapAddNumber(a,7,3); for(int i=0;i<8;++i)cout<<a[i]<<" "; MinHeapDeleteNumber(a,8); cout<<endl<<"刪除頂點後的資料為:"; for(int i=0;i<7;++i)cout<<a[i]<<" "; return 0;}
我們自然要問:維護一個堆結構的意義何在?至少有兩個目的:堆排序和優先隊列。四 ,堆排序。思想:既然我們可以每次取出頂點的資料,取到的資料又是最小資料,根據此思想,我們可以把數組裡的資料全部取完後得到的資料就是有順序的了。不過跟歸併排序一樣,空間複雜度增加了,必須有一個數組去接受。測試:
int b[8]; for(int i=0;i<8;++i){b[i]=a[0]; MinHeapDeleteNumber(a,8-i);}for(int i=0;i<8;++i)cout<<b[i]<<" ";
我們知道,任何演算法都得用時間複雜度計算一下,否則,演算法沒什麼意義。由於按樹堆化,樹高lgN,最壞情況下,每層計算N次,所以複雜度為:O(NlgN),跟歸併一樣。最優隊列:最優隊列是每次都彈出資料的最大或是最小值。每次進隊列後,都要一次更新操作。每次出隊列都要一次刪除操作。