Java實現堆排序(大根堆)
堆排序是一種樹形選擇排序方法,它的特點是:在排序的過程中,將array[0,...,n-1]看成是一顆完全二叉樹的順序存儲結構,利用完全二叉樹中雙親節點和孩子結點之間的內在關系,在當前無序區中選擇關鍵字最大(最小)的元素。
1. 若array[0,...,n-1]表示一顆完全二叉樹的順序存儲模式,則雙親節點指針和孩子結點指針之間的內在關系如下:
任意一節點指針 i:父節點:i==0 ? null : (i-1)/2
左孩子:2*i + 1
右孩子:2*i + 2
2. 堆的定義:n個關鍵字序列array[0,...,n-1],當且僅當滿足下列要求:(0 <= i <= (n-1)/2)
① array[i] <= array[2*i + 1] 且 array[i] <= array[2*i + 2]; 稱為小根堆;
② array[i] >= array[2*i + 1] 且 array[i] >= array[2*i + 2]; 稱為大根堆;
3. 建立大根堆:
n個節點的完全二叉樹array[0,...,n-1],最后一個節點n-1是第(n-1-1)/2個節點的孩子。對第(n-1-1)/2個節點為根的子樹調整,使該子樹稱為堆。
對於大根堆,調整方法為:若【根節點的關鍵字】小於【左右子女中關鍵字較大者】,則交換。
之后向前依次對各節點((n-2)/2 - 1)~ 0為根的子樹進行調整,看該節點值是否大於其左右子節點的值,若不是,將左右子節點中較大值與之交換,交換后可能會破壞下一級堆,於是繼續采用上述方法構建下一級的堆,直到以該節點為根的子樹構成堆為止。
反復利用上述調整堆的方法建堆,直到根節點。
4.堆排序:(大根堆)
①將存放在array[0,...,n-1]中的n個元素建成初始堆;
②將堆頂元素與堆底元素進行交換,則序列的最大值即已放到正確的位置;
③但此時堆被破壞,將堆頂元素向下調整使其繼續保持大根堆的性質,再重復第②③步,直到堆中僅剩下一個元素為止。
堆排序算法的性能分析:
空間復雜度:o(1);
時間復雜度:建堆:o(n),每次調整o(log n),故最好、最壞、平均情況下:o(n*logn);
穩定性:不穩定
建立大根堆的方法:
1 //構建大根堆:將array看成完全二叉樹的順序存儲結構
2 private int[] buildMaxHeap(int[] array){
3 //從最后一個節點array.length-1的父節點(array.length-1-1)/2開始,直到根節點0,反復調整堆
4 for(int i=(array.length-2)/2;i>=0;i--){
5 adjustDownToUp(array, i,array.length);
6 }
7 return array;
8 }
9
10 //將元素array[k]自下往上逐步調整樹形結構
11 private void adjustDownToUp(int[] array,int k,int length){
12 int temp = array[k];
13 for(int i=2*k+1; i<length-1; i=2*i+1){ //i為初始化為節點k的左孩子,沿節點較大的子節點向下調整
14 if(i<length && array[i]<array[i+1]){ //取節點較大的子節點的下標
15 i++; //如果節點的右孩子>左孩子,則取右孩子節點的下標
16 }
17 if(temp>=array[i]){ //根節點 >=左右子女中關鍵字較大者,調整結束
18 break;
19 }else{ //根節點 <左右子女中關鍵字較大者
20 array[k] = array[i]; //將左右子結點中較大值array[i]調整到雙親節點上
21 k = i; //【關鍵】修改k值,以便繼續向下調整
22 }
23 }
24 array[k] = temp; //被調整的結點的值放人最終位置
25 }
堆排序:
1 //堆排序
2 public int[] heapSort(int[] array){
3 array = buildMaxHeap(array); //初始建堆,array[0]為第一趟值最大的元素
4 for(int i=array.length-1;i>1;i--){
5 int temp = array[0]; //將堆頂元素和堆低元素交換,即得到當前最大元素正確的排序位置
6 array[0] = array[i];
7 array[i] = temp;
8 adjustDownToUp(array, 0,i); //整理,將剩余的元素整理成堆
9 }
10 return array;
11 }
刪除堆頂元素(即序列中的最大值):先將堆的最后一個元素與堆頂元素交換,由於此時堆的性質被破壞,需對此時的根節點進行向下調整操作。
1 //刪除堆頂元素操作
2 public int[] deleteMax(int[] array){
3 //將堆的最后一個元素與堆頂元素交換,堆底元素值設為-99999
4 array[0] = array[array.length-1];
5 array[array.length-1] = -99999;
6 //對此時的根節點進行向下調整
7 adjustDownToUp(array, 0, array.length);
8 return array;
9 }
對堆的插入操作:先將新節點放在堆的末端,再對這個新節點執行向上調整操作。
假設數組的最后一個元素array[array.length-1]為空,新插入的結點初始時放置在此處。
1 //插入操作:向大根堆array中插入數據data
2 public int[] insertData(int[] array, int data){
3 array[array.length-1] = data; //將新節點放在堆的末端
4 int k = array.length-1; //需要調整的節點
5 int parent = (k-1)/2; //雙親節點
6 while(parent >=0 && data>array[parent]){
7 array[k] = array[parent]; //雙親節點下調
8 k = parent;
9 if(parent != 0){
10 parent = (parent-1)/2; //繼續向上比較
11 }else{ //根節點已調整完畢,跳出循環
12 break;
13 }
14 }
15 array[k] = data; //將插入的結點放到正確的位置
16 return array;
17 }
測試:
1 public void toString(int[] array){
2 for(int i:array){
3 System.out.print(i+" ");
4 }
5 }
6
7 public static void main(String args[]){
8 HeapSort hs = new HeapSort();
9 int[] array = {87,45,78,32,17,65,53,9,122};
10 System.out.print("構建大根堆:");
11 hs.toString(hs.buildMaxHeap(array));
12 System.out.print("\n"+"刪除堆頂元素:");
13 hs.toString(hs.deleteMax(array));
14 System.out.print("\n"+"插入元素63:");
15 hs.toString(hs.insertData(array, 63));
16 System.out.print("\n"+"大根堆排序:");
17 hs.toString(hs.heapSort(array));
18 }

