Algorithm——堆排序算法(八)

Algorithm——堆排序算法


在分析堆排序之前,我們先來回顧一些基本概念:在計算機科學中,二叉樹是每個節點最多有兩個子樹的樹結構。通常子樹被稱作“左子樹”(left subtree)和“右子樹”(right subtree)。二叉樹常被用於實現二叉查找樹和二叉堆。而完全二叉樹:葉節點只能出現在最下層和次下層,並且最下面一層的結點都集中在該層最左邊的若干位置的二叉樹(即葉子結點都是從左到右依次排布)。

堆是一個數組,它可以被看成一個近似的完全二叉樹。樹的每一個節點對應數組中的一個元素。除了最底層外,該樹是完全滿的,而且是從左向右填充。表示堆的數組A包括兩個屬性:

  • A.length:給出數組元素的個數
  • A.heap-size:表示有多少個堆元素存儲在該數組中。也就是說,雖然A[1...A.length]可能都存有數據,但只有A[1...A.heap-size]中存放的是堆的有效數據,這裏0=<A.heap-size=<A.length;在實際算法實現中,使用heap-size屬性可以剔除掉已經交換得到的最大值元素。

同時堆也分爲最大堆和最小堆。在最大堆中,最大堆性質是指除了根以外的所以節點i都要滿足:A[PARENT(i)]>=A[i]。也就是說,某個節點的值至多與其父節點一樣大。因此,堆中的最大元素存放根節點中;且,在任意子樹中,該子樹所包含的所有節點的值都不大於該子樹根節點的值。最小堆的組織方式正好相反:最小堆性質是指除了根以外的所有節點i都有A[PARENT(i)]=<A[i]。

通常,堆排序算法中使用最大堆;最小堆則用於構造優先隊列。

用Java實現的堆排序算法如下所示:

	/**
	 * 堆排序
	 * 
	 * @param A
	 */
	public void heapSort(int[] A) {
		int heap_size = A.length - 1;
		buildMaxHeap(A, heap_size);
		for (int i = A.length - 1; i > 0; i--) {
			exchange(A, 0, i);
			heap_size--;
			maxHeapify(A, heap_size, 0);
		}
	}

	/**
	 * 
	 * 建立初始最大堆
	 * 
	 * 由完全二叉樹的性質可以推出:如果要將數組A[1...n]轉換成最大堆,則子數組元素A[n/2 + 1...n]都是樹的葉子節點,且葉子節點可以看做是隻有一個元素的堆
	 * 
	 * @param A
	 */
	public void buildMaxHeap(int[] A, int heap_size) {
		for (int i = A.length / 2 - 1; i >= 0; i--) {
			maxHeapify(A, heap_size, i);
		}
	}

	/**
	 * 堆排序中使用最大堆性質
	 * 
	 * 維護最大堆性質:以iMax節點爲根節點,維護此子二叉樹中的最大堆性質
	 * 
	 * @param A
	 *            排序的數組
	 * @param idex
	 *            操作的子樹的根節點對應的下標值
	 */
	public void maxHeapify(int[] A, int heap_size, int iMax) {

		int left = getLeftIndex(iMax);// 該根節點的左節點元素下標
		int right = getRightIndex(iMax);// 該根節點的右節點元素下標

		int largest = iMax;

		if (heap_size >= left && A[left] > A[iMax])// 判斷根節點下標的元素與左子節點下標的元素哪個大;largest存放較大的元素的下標
			largest = left;
		else
			largest = iMax;

		if (heap_size >= right && A[right] > A[largest])// 判斷largest下標的代表元素與右節點下標的元素哪個大;largest存放較大的元素的下標
			largest = right;

		if (iMax != largest) {// 如果largest與iMax不等,說明根節點的元素並不是最大的;隨即進行元素調換,保證根節點的元素值是根節點/左右子節點中最大的那個
			exchange(A, iMax, largest);
			maxHeapify(A, heap_size, largest);// 在遞歸調用maxHeapify()函,使以largest下標元素爲根節點的子樹也保證堆最大性質
		}
	}

	public void exchange(int[] A, int idex, int idex2) {
		int temp = A[idex];
		A[idex] = A[idex2];
		A[idex2] = temp;

	}

	/**
	 * 
	 * 獲取下標i對應元素的父元素下標
	 * 
	 * @param i
	 * @return
	 */
	public int getParentIndex(int iMax) {
		return (iMax == 0) ? 0 : (iMax % 2 == 0 ? (iMax - 1 / 2) : (iMax / 2));
	}

	/**
	 * 
	 * 獲取下標i代表的父元素的左子元素的下標
	 * 
	 * @param i
	 * @return
	 */
	public int getLeftIndex(int iMax) {
		return 2 * iMax + 1;
	}

	/**
	 * 獲取下標i代表的父元素的右子元素的下標
	 * 
	 * @param i
	 * @return
	 */
	public int getRightIndex(int iMax) {
		return 2 * (iMax + 1);
	}
堆排序算法完整的測試代碼如下:
public class SortAlgor {

	public static void main(String[] args) {
		int[] array = { 2,5,10,8,7 };
		SortAlgor algorithm = new SortAlgor();
		algorithm.heapSort(array);
		algorithm.arrayPrint(array);
	}

	/**
	 * 堆排序
	 * 
	 * @param A
	 */
	public void heapSort(int[] A) {
		int heap_size = A.length - 1;
		buildMaxHeap(A, heap_size);
		for (int i = A.length - 1; i > 0; i--) {
			exchange(A, 0, i);
			heap_size--;
			maxHeapify(A, heap_size, 0);
		}
	}

	/**
	 * 
	 * 建立初始最大堆
	 * 
	 * 由完全二叉樹的性質可以推出:如果要將數組A[1...n]轉換成最大堆,則子數組元素A[n/2 + 1...n]都是樹的葉子節點,且葉子節點可以看做是隻有一個元素的堆
	 * 
	 * @param A
	 */
	public void buildMaxHeap(int[] A, int heap_size) {
		for (int i = A.length / 2 - 1; i >= 0; i--) {
			maxHeapify(A, heap_size, i);
		}
	}

	/**
	 * 堆排序中使用最大堆性質
	 * 
	 * 維護最大堆性質:以iMax節點爲根節點,維護此子二叉樹中的最大堆性質
	 * 
	 * @param A
	 *            排序的數組
	 * @param idex
	 *            操作的子樹的根節點對應的下標值
	 */
	public void maxHeapify(int[] A, int heap_size, int iMax) {

		int left = getLeftIndex(iMax);// 該根節點的左節點元素下標
		int right = getRightIndex(iMax);// 該根節點的右節點元素下標

		int largest = iMax;

		if (heap_size >= left && A[left] > A[iMax])// 判斷根節點下標的元素與左子節點下標的元素哪個大;largest存放較大的元素的下標
			largest = left;
		else
			largest = iMax;

		if (heap_size >= right && A[right] > A[largest])// 判斷largest下標的代表元素與右節點下標的元素哪個大;largest存放較大的元素的下標
			largest = right;

		if (iMax != largest) {// 如果largest與iMax不等,說明根節點的元素並不是最大的;隨即進行元素調換,保證根節點的元素值是根節點/左右子節點中最大的那個
			exchange(A, iMax, largest);
			maxHeapify(A, heap_size, largest);// 在遞歸調用maxHeapify()函,使以largest下標元素爲根節點的子樹也保證堆最大性質
		}
	}

	public void exchange(int[] A, int idex, int idex2) {
		int temp = A[idex];
		A[idex] = A[idex2];
		A[idex2] = temp;

	}

	/**
	 * 
	 * 獲取下標i對應元素的父元素下標
	 * 
	 * @param i
	 * @return
	 */
	public int getParentIndex(int iMax) {
		return iMax;
	}

	/**
	 * 
	 * 獲取下標i代表的父元素的左子元素的下標
	 * 
	 * @param i
	 * @return
	 */
	public int getLeftIndex(int iMax) {
		return 2 * iMax + 1;
	}

	/**
	 * 獲取下標i代表的父元素的右子元素的下標
	 * 
	 * @param i
	 * @return
	 */
	public int getRightIndex(int iMax) {
		return 2 * (iMax + 1);
	}

	public void arrayPrint(int[] array) {
		System.out.print("[");
		for (int i = 0; i < array.length; i++) {
			System.out.print(array[i] + ", ");
		}
		System.out.println("]");
	}
}
本來是會上一點圖示,來展示堆排序算法的運行過程的;但網站貌似有問題,圖片插入了無法顯示。後面補上吧~~


PS:

下面是堆排序的一個運行過程的圖示:




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章