(二叉)堆(heap)資料結構是一種數組對象,可以視作一顆完全二叉樹,從該二叉樹的根開始層次遍曆這顆二叉樹就可以得到其對應的數組。樹的根節點為A[0],對於樹中某個節點的座標i,其左右孩子節點和父親節點的座標可以很方便的求得:
LEFT(i)=2*i+1; RIGHT(i)=2*i+2; PARENT(i)=i/2 .
有兩種二元堆積:最大堆和最小堆。最大堆中,每個節點儲存的數值都大於等於其左右兩個孩子節點儲存的數值,亦即A[i]>=A[LEFT[i]]&&A[i]>=A[RIGHT[i]]。最小堆則正好相反。本文以最大堆為例。
知道了最大堆的定義之後,就要在給定的任意數組上構建最大堆,然後利用這個最大堆進行升序排序:
(1) 構建最大堆:
首先我們定義一個方法Heapify(heap,i),如果以i的左右孩子節點為根的子樹已經是最大堆,則該方法使得以i為根的子樹也成為最大堆。其虛擬碼如下(摘自《演算法導論》):
1 MAX-HEAPIFY(A,i) 2 l=LEFT(i) 3 r=RIGHT(i) 4 target=i; 5 if(l<=A.heap_size&&A[l]>A[i]) 6 target=l; 7 if(if(r<=A.heap_size&&A[r]>A[i])) 8 target=r; 9 if(target!=i)10 exchange(A[i],A[target])11 MAX-HEAPIFY(A,target)//遞迴
我們比較當前節點i的數值和其左右子節點的數值,如果i的某個子節點的數值大於i的數值,則違反了最大堆的定義,所以我們需要交換這兩個節點的位置。假設A[LEFT[i]]>A[RIGHT[i]]>A[i],則交換i節點與LEFT[i]節點。但是,被交換到左孩子節點的i節點可能會違反以左孩子結點為根的最大堆的定義,所以我們需要對這個最大堆遞迴調用MAX-HEAPIFY方法。
有了上面這個方法之後,我們就可以自底向上的調用MAX-HEAPIFY方法構建最大堆。因為A[A.length/2+1]及其之後的節點都是葉子節點,都可以看做只有一個節點的最大堆,所以我們可以從A[A.length/2]節點開始直到A[0]節點依次調用MAX-HEAPIFY方法,亦即從樹的層次遍曆的最後一個有孩子的節點開始,按照層次遍曆的逆序調用MAX-HEAPIFY方法:
1 BUILD-MAX-HEAP(A)2 for i=A.length/2 downto 13 MAX-HEAPIFY(A,i)
(2) 堆排序
構造完最大堆之後,我們就可以利用其進行排序。因為最大堆只能保證A[0]儲存的是當前堆中最大的元素,我們可以把A[0]與堆的最後一個元素互換,這樣A[0]就排在了最後一個位置,也是正確的位置。這時最後一個位置已經不屬於最大堆,所以A.heap_size要減一。互換到A[0]的元素可能會破壞最大堆的性質,我們可以調用MAX-HEAPIFY方法使之重新成為最大堆,然後將A[0]交換至當前堆的最後一個位置。依次遞迴。
1 HEAPSORT(A)2 for i=A.length downto 23 exchange(A[i],A[0])4 --A.heap_size //堆的大小減少5 MAX-HEAPIFY(A,0)
(3) 堆的JAVA實現
1 package Algorithm; 2 3 import java.util.*; 4 /** 5 * 堆(Heap) 6 * @author Kemaswill 7 * 2012-10-5 Friday 8 */ 9 10 public class Heap {11 12 public static int[] data;13 public static int length;14 public static int heap_size;15 16 public Heap(){17 this.length=20;18 this.heap_size=length;19 this.data=new int[length];20 Random random=new Random(System.currentTimeMillis());21 for(int i=0;i<length;i++){22 data[i]=Math.abs(random.nextInt()%50);23 }//for24 }25 26 public Heap(int n){27 this.length=n;28 this.heap_size=length;29 this.data=new int[length];30 Random random=new Random(System.currentTimeMillis());31 for(int i=0;i<length;i++){32 data[i]=Math.abs(random.nextInt()%50);33 }//for34 }35 36 public static void max_heapify(Heap heap,int i){37 if(i<heap.heap_size){38 int l=2*i+1;39 int r=2*i+2;40 int target=i;41 if(l<heap.heap_size&&heap.data[l]>heap.data[i]){42 target=l;43 }44 if(r<heap.heap_size&&heap.data[r]>heap.data[target]){45 target=r;46 }47 if(target!=i){48 exchange(heap,i,target);49 max_heapify(heap,target);50 }//if51 }//if 52 }//heapify53 54 public static void exchange(Heap heap,int x,int y){55 int tmp=heap.data[x];56 heap.data[x]=heap.data[y];57 heap.data[y]=tmp;58 }59 60 public static void build_heap(Heap heap){61 //對於所有非葉結點,依次調用max_heapify62 for(int i=heap.heap_size/2;i>=0;i--){63 max_heapify(heap,i);64 }65 }66 67 public static void heapsort(Heap heap){68 69 for(int i=heap.length-1;i>0;i--){70 exchange(heap,0,i);71 heap.heap_size--;72 max_heapify(heap,0);73 }74 }//heapsotr75 76 public static void show_heap(Heap heap){77 for(int i=0;i<=(int)Math.log(heap.length)/Math.log(2)+2;i++){78 for(int j=(int)Math.pow(2, i)-1;j<Math.pow(2, i+1)-1;j++){79 if(j<heap.length){80 System.out.print(heap.data[j]+" ");81 }82 else break;83 }84 System.out.println();85 } 86 }87 88 public static void main(String[] args){89 Heap heap=new Heap();90 show_heap(heap);91 build_heap(heap);92 show_heap(heap);93 heapsort(heap);94 show_heap(heap);95 96 }//main97 98 }
參考文獻:
[1] 《演算法導論》 第6章 p73
[2] 一個用Java實現的簡單的最大堆