1、堆排序概述
堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。可以利用數組的特點快速定位指定索引的元素。堆分為大根堆和小根堆,是完全二叉樹。大根堆的要求是每個節點的值都不大於其父節點的值,即A[PARENT[i]] >= A[i]。在數組的非降序排序中,需要使用的就是大根堆,因為根據大根堆的要求可知,最大的值一定在堆頂。
2、堆排序思想(大根堆)
1)先將初始文件Array[1...n]建成一個大根堆,此堆為初始的無序區。
2) 再將關鍵字最大的記錄Array[1](即堆頂)和無序區的最后一個記錄Array[n]交換,由此得到新的無序區Array[1..n-1]和有序區Array[n],且滿足Array[1..n-1].keys≤Array[n].key。
3)由於交換后新的根R[1]可能違反堆性質,故應將當前無序區R[1..n-1]調整為堆。然后再次將R[1..n-1]中關鍵字最大的記錄R[1]和該區間的最后一個記錄R[n-1]交換,由此得到新的無序區R[1..n-2]和有序區R[n-1..n],且仍滿足關系R[1..n-2].keys≤R[n-1..n].keys,同樣要將R[1..n-2]調整為堆。這樣直到無序區中剩余一個元素為止。
3、堆排序的基本操作
1)建堆,建堆是不斷調整堆的過程,從len/2處開始調整,一直到第一個節點,此處len是堆中元素的個數。建堆的過程是線性的過程,從len/2到0處一直調用調整堆的過程,相當於o(h1)+o(h2)…+o(hlen/2) 其中h表示節點的深度,len/2表示節點的個數,這是一個求和的過程,結果是線性的O(n)。
2)調整堆:調整堆在構建堆的過程中會用到,而且在堆排序過程中也會用到。利用的思想是比較節點i和它的孩子節點left(i),right(i),選出三者最大者,如果最大值不是節點i而是它的一個孩子節點,那邊交互節點i和該節點,然后再調用調整堆過程,這是一個遞歸的過程。調整堆的過程時間復雜度與堆的深度有關系,是lgn的操作,因為是沿着深度方向進行調整的。
3)堆排序:堆排序是利用上面的兩個過程來進行的。首先是根據元素構建堆。然后將堆的根節點取出(一般是與最后一個節點進行交換),將前面len-1個節點繼續進行堆調整的過程,然后再將根節點取出,這樣一直到所有節點都取出。堆排序過程的時間復雜度是O(nlgn)。因為建堆的時間復雜度是O(n)(調用一次);調整堆的時間復雜度是lgn,調用了n-1次,所以堆排序的時間復雜度是O(nlgn)
4、堆排序的實現(java)
1 package com.arithmetic.demo; 2 3 public class HeapSort { 4 //交換元素 5 public static void exchange(int[] array,int i,int j) 6 { 7 int temp = array[i]; 8 array[i] = array[j]; 9 array[j] = temp; 10 } 11 //獲得父節點 12 public static int parentNode(int i) 13 { 14 return (i - 1)/2; 15 } 16 //獲得左節點 17 public static int leftNode(int i) 18 { 19 return 2*i + 1; 20 } 21 //獲得右節點 22 public static int rightNode(int i) 23 { 24 return 2*i + 2; 25 } 26 //最大堆 27 public static void maxHeap(int[] array,int heapSize,int index) 28 { 29 int left = leftNode(index); 30 int right = rightNode(index); 31 int largeNode = index; 32 if(left < heapSize && array[left] > array[largeNode]) 33 { 34 largeNode = left; 35 } 36 if(right < heapSize && array[right] > array[largeNode]) 37 { 38 largeNode = right; 39 } 40 if(index != largeNode) 41 { 42 exchange(array, index, largeNode); 43 maxHeap(array,heapSize,largeNode); 44 } 45 } 46 public static void buildHeap(int[] array) 47 { 48 if(array == null || array.length <= 1){ 49 return; 50 } 51 int half = array.length/2; 52 for(int i=half;i>=0;i--) 53 { 54 maxHeap(array,array.length,i); 55 } 56 } 57 public static void heapSort(int[] array) 58 { 59 if(array == null || array.length <= 1) 60 { 61 return; 62 } 63 buildHeap(array); 64 for(int i=array.length -1;i>=1;i--) 65 { 66 exchange(array,0,i); 67 maxHeap(array,i,0); 68 } 69 } 70 public static void printHeapTree(int[] array) 71 { 72 for(int i=1;i<=array.length;i=i*2) 73 { 74 for(int k=i-1;k<2*i-1 && k<array.length;k++) 75 { 76 System.out.print(array[k]+" "); 77 } 78 System.out.println(); 79 } 80 } 81 public static void printHeap(int[] array) 82 { 83 for(int i=0;i<array.length;i++) 84 { 85 System.out.print(array[i]+" "); 86 } 87 } 88 public static void main(String[] args) 89 { 90 int[] array = {7,4,5,3,2,6,9,1}; 91 System.out.println("執行最大堆化前,堆結構:"); 92 printHeapTree(array); 93 94 buildHeap(array); 95 96 System.out.println("執行最大堆化后,堆結構:"); 97 printHeapTree(array); 98 99 heapSort(array); 100 101 System.out.println("堆排序結果:"); 102 printHeap(array); 103 } 104 105 }
5、算法分析
堆排序的時間,主要由建立初始堆和反復重建堆這兩部分的時間開銷構成,它們均是通過調用buildHeap實現的。它是不穩定的排序方法。平均性能:O(N*logN)。
