一、定義
堆的定義
堆其實就是一棵完全二叉樹(若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊),
定義為:具有n個元素的序列(h1,h2,...hn),當且僅當滿足(hi>=h2i,hi>=h2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)時稱之為堆
大頂堆
堆頂元素(即第一個元素)為最大項,並且(hi>=h2i,hi>=h2i+1)
小頂堆
堆頂元素為最小項,並且(hi<=h2i,hi<=2i+1)
二、構建堆(大頂堆)
方法
序列對應一個完全二叉樹,從最后一個分支節點(n div 2)開始,到跟(1)為止,一次對每個分支節點進行調整(下沉),以便形成以每個分支節點為根的堆,當最后對樹根節點進行調整后,整個樹就變成一個堆
實例
先給出一個序列:45,36,18,53,72,30,48,93,15,35
要想此序列稱為一個堆,我們按照上述方法,首先從最后一個分支節點(10/2),其值為72開始,一次對每個分支節點53,18,36,45進行調整(下沉)
圖解流程
代碼實現
/*根據樹的性質建堆,樹節點前一半一定是分支節點,即有孩子的,所以我們從這里開始調整出初始堆*/
public static void adjust(List<Integer> heap){
for (int i = heap.size() / 2; i > 0; i--)
adjust(heap,i, heap.size()-1);
System.out.println("=================================================");
System.out.println("調整后的初始堆:");
print(heap);
}
/**
* 調整堆,使其滿足堆得定義
* @param i
* @param n
*/
public static void adjust(List<Integer> heap,int i, int n) {
int child;
for (; i <= n / 2; ) {
child = i * 2;
if(child+1<=n&&heap.get(child)<heap.get(child+1))
child+=1;/*使child指向值較大的孩子*/
if(heap.get(i)< heap.get(child)){
swap(heap,i, child);
/*交換后,以child為根的子樹不一定滿足堆定義,所以從child處開始調整*/
i = child;
} else break;
}
}
//把list中的a,b位置的值互換
public static void swap(List<Integer> heap, int a, int b) {
//臨時存儲child位置的值
int temp = (Integer) heap.get(a);
//把index的值賦給child的位置
heap.set(a, heap.get(b));
//把原來的child位置的數值賦值給index位置
heap.set(b, temp);
}
三、堆排序
堆排序的性能介紹(適合處理數據量大的序列)
由於它在直接選擇排序的基礎上利用了比較結果形成。效率提高很大。它完成排序的總比較次數為O(nlog2n)。
堆排序需要兩個步驟,一個建堆,而是交換重新建堆。比較復雜,所以一般在小規模的序列中不合適,但對於較大的序列,將表現出優越的性能
算法描述(建堆,交換重新建堆):
- 初始時把要排序的數的序列看作是一棵順序存儲的二叉樹,調整它們的存儲序,使之成為一個 堆,這時堆的根節點的數最大
- 然后將根節點與堆的最后一個節點交換。然后對前面(n-1)個數重新調整使之成為堆
- 依此類推,直到只有兩個節點的堆,並對 它們作交換,最后得到有n個節點的有序序列
代碼實現
//對一個最大堆heap排序
public static void heapSort(List<Integer> heap) {
for (int i = heap.size()-1; i > 0; i--) {
/*把根節點跟最后一個元素交換位置,調整剩下的n-1個節點,即可排好序*/
swap(heap,1, i);
adjust(heap,1, i - 1);
}
}
四、堆排序的應用
場景:
如何從100萬個數中找出最大的前100個數
算法分析:
先取出前100個數,維護一個100個數的最小堆,遍歷一遍剩余的元素,在此過程中維護堆就可以了。
- 取前m個元素(例如m=100),建立一個小頂堆
- 順序讀取后續元素,直到結束。每次讀取一個元素,如果該元素比堆頂元素小,直接丟棄
如果大於堆頂元素,則用該元素替換堆頂元素,然后保持最小堆性質。 - 最后這個堆中的元素就是前最大的100個