1. 程式人生 > >排序演算法之堆排序—原地排序O(nlgn)

排序演算法之堆排序—原地排序O(nlgn)

《演算法導論》第6章介紹堆排序(heapsort)像插入排序而不像合併排序,是一種原地(in place)排序演算法:任何時候,陣列中只有常數個元素儲存在輸入陣列之外。並且時間複雜度為O(nlgn)。 堆資料結構不只在堆排序中有用,還可以構成一個有效的優先佇列。

堆介紹

(二叉)堆資料結構是一種陣列物件。可以被視為一棵完全二叉樹。每個節點都有兩個子結點,每一層都是填滿的最後一層除外,因此可以用陣列表示即可。(《演算法導論》中指出,length為此陣列中的元素個數,heap_size為此陣列中堆的元素個數。就是說,heap_size以後的陣列元素不屬於堆內,不符合堆的要求。) 樹的根為A[1],給定一個數組位置i,它的父節點為i/2」,左兒子為2i,右兒子為2i+1。

堆實現

堆的高度為O(lgn),一些基本操作可以在O(lgn)內完成。 注:以下演算法假設陣列a下標從1開始,a[0]不參與計算,heap_size為陣列中堆的大小。

MAX-HEAPIFY過程,保持最大堆性質的關鍵

輸入一個數組A和下標i,並且假定以i為父節點的所有子結點滿足堆性質。 輸出使得i結點保持堆性質。

	/**
     * 輸入:預設A陣列中i後面的位置都滿足最大堆性質
     * 輸出:使得i也滿足最大堆性質
     * 時間複雜度:O(lgn)
     */
		private static void MaxHeapify(int[] a, int i, int heap_size)
{ int left = 2 * i; //左兒子 int right = 2 * i+1; //右兒子 int largest = i; //i和左兒子、右兒子中最大值的位置 //如果左兒子比i大,則把左兒子做為最大值 if( left <= heap_size && a[left] > a[i]) { largest = left; } //如果右兒子比最大值大,則把右兒子做為最大值位置 if( right <= heap_size && a[right] > a[largest]) {
largest = right; } //如果最大值位置不是i,則將最大值位置的值與i位置的值交換,交換後需要被交換的子結點繼續保持堆性質 if ( largest != i) { int tmp = a[i]; a[i] = a[largest]; a[largest] = tmp; MaxHeapify(a, largest, heap_size); } }

BUILD-MAX-HEAP過程,可以在無序的陣列上構造堆

	/**
	 * 自底向上的用MaxHeapify將一個數組變成最大堆
	 * 時間複雜度O(nlgn)
	 */
    private static void BuildMaxHeap(int[] a) {
    	//堆底為葉結點肯定都滿足性質,從非葉結點開始構建。
    	int heap_size = a.length - 1;
    	int i = heap_size/2;
    	for (; i>0; i--)
    		MaxHeapify(a, i, heap_size);
	}

HEAPSORT過程,執行時間為O(nlgn)

排序演算法每次從堆頂取出最大值,與陣列末尾值交換即可,並縮小heap的大小,將末尾值排除在堆外。當堆縮小為1時,陣列已完成從小到大排序。

	private static void HeapSort(int[] a) {
		BuildMaxHeap(a);
	    for(int i=0;i<a.length;i++) {
	    	System.out.println(a[i]);
	    }
		int heap_size = a.length-1;   //構造後的堆的heap_size為陣列大小
		for(; heap_size > 0; heap_size--){//每次從堆頂取出最大值與堆末尾交換,並縮減堆大小
			int tmp = a[heap_size];
			a[heap_size] = a[1];
			a[1] = tmp;
			MaxHeapify(a, 1, heap_size-1);
		}
	}

優先順序佇列

《演算法導論》第6章第5節優先順序佇列中指出,“實際中快速排序的一個好的實現往往由於堆排序。儘管這樣,堆資料結構還是有著很大的用處:作為高效的優先順序佇列(priority queue)。”【檢視JAVA8原始碼可知,Arrays.sort也使用的快速排序(小於47個數的時候用的插入排序)。】 “最大優先順序佇列的一個應用是在一臺分時計算機上進行作業排程。當一個作業做完或被中斷時,用EXTRACT-MAX操作從所有等待的作業中,選擇出具有最高優先順序的作業”