堆其實就是一棵徹底二叉樹(若設二叉樹的深度爲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)3d
堆頂元素爲最小項,而且(hi<=h2i,hi<=2i+1)code
序列對應一個徹底二叉樹,從最後一個分支節點(n div 2)開始,到跟(1)爲止,一次對每一個分支節點進行調整(下沉),以便造成以每一個分支節點爲根的堆,當最後對樹根節點進行調整後,整個樹就變成一個堆blog
先給出一個序列:45,36,18,53,72,30,48,93,15,35排序
要想此序列稱爲一個堆,咱們按照上述方法,首先從最後一個分支節點(10/2),其值爲72開始,一次對每一個分支節點53,18,36,45進行調整(下沉)get
圖解流程
代碼實現效率
/*根據樹的性質建堆,樹節點前一半必定是分支節點,即有孩子的,因此咱們從這裏開始調整出初始堆*/ 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)。基礎
堆排序須要兩個步驟,一個建堆,而是交換從新建堆。比較複雜,因此通常在小規模的序列中不合適,但對於較大的序列,將表現出優越的性能
List
//對一個最大堆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個數的最小堆,遍歷一遍剩餘的元素,在此過程當中維護堆就能夠了。