排序算法:堆排序

堆排序:不穩定排序

堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。可以利用數組的特點快速定位指定索引的元素。堆分爲大根堆和小根堆,是完全二叉樹

 思想:初始時把要排序的數的序列看作是一棵順序存儲的二叉樹,調整它們的存儲序,使之成爲一個 堆,這時堆的根節點的數最大。然後將根節點與堆的最後一個節點交換。然後對前面(n-1)個數重新調整使之成爲堆。依此類推,直到只有兩個節點的堆,並對 它們作交換,最後得到有n個節點的有序序列。從算法描述來看,堆排序需要兩個過程,一是建立堆,二是堆頂與堆的最後一個元素交換位置。所以堆排序有兩個函數組成。一是建堆的滲透函數,二是反覆調用滲透函數實現排序的函數。

由於建初始堆所需的比較次數較多,所以堆排序不適宜於記錄數較少的文件。

時間複雜度:

最差:O(nlog2n)

平均:O(nlog2n)

最好:O(nlog2n)

空間複雜度: O(1)

package test;

import java.util.Arrays;
/*
 * 堆排序
 * 大頂堆的特點:父節點比左右節點的值都大的完全二叉樹
 * 數組中可以按下標來確定在二叉樹的位置,例如,位置爲i的父節點,其子節點位置是2*i 和 2*i+1
 * 所以可以直接將數組當成二叉樹來處理,不用另外建樹。注意,數組的第一個元素A[0] 不在二叉樹的範圍內
 * 二叉樹應滿足 父節點 大於子節點,所以從最後一個非葉節點開始往前遍歷,來調整堆
 * (注意:當交換節點位置時,需要重新調整子節點樹,因爲經過交換後,其子樹可能不符合堆的要求)
 * 
 * 當二叉樹滿足大頂堆時,堆頂的元素就是最大的元素。從而可以利用這個來排序
 * 將堆頂元素與對的最後一個元素交換,然後對前面(n-1)個元素重新調整,就可以變成新的堆。重複交換即可排序
 */
public class HeapSort {
	//調整堆的函數,i爲需要調整的子樹的父節點
	static void heapAdjust(int a[], int i, int size) {
		//左右節點的下標
		int l = 2* i;
		int r = 2*i + 1;
		//尋找父節點,左節點,右節點中最大的元素的下標,注意左右節點的下標不能越界
		int max = i;
		if(l <= size && a[l] > a[max]) {
			max = l;
		}
		if(r <= size && a[r] > a[max]) {
			max = r;
		}
		//max != i 表明父節點不是最大的,需要交換該子節點A[max] 和父節點,並且重新調整該子節點的子樹
		if(max != i) {
			int temp = a[i];
			a[i] = a[max];
			a[max] = temp;
			//調整子節點的子樹
			heapAdjust(a,max,size);
		}
		
	}
	
	//建堆函數:初始化數組時,從最後一個非葉子節點開始往前掃描,逐一調整堆,直到A[1],表明建堆完畢
	static void heapBuild(int a[], int size) {
		for(int i = size/2; i >= 1; i--) {
			heapAdjust(a,i,size);
		}
	}
	
	//堆排序函數
	static void heapSort(int a[], int size) {
		//首先建堆
		heapBuild(a,size);
		//從最後一個元素開始,與堆頂元素交換,得到最大的數。前面n-1個數重新調整爲堆,再繼續交換
		for(int i = size; i >= 1; i--) {
			int temp = a[1];
			a[1] = a[i];
			a[i] = temp;
			heapAdjust(a,1,i-1);
		}
	}
	
	public static void main(String args[]) {
		int a[] = new int[]{0,16,7,3,20,17,8};
		heapSort(a,6);
		System.out.println(Arrays.toString(a));
	}
	
}



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