1. 程式人生 > >堆排序(Java實現)

堆排序(Java實現)

堆排序指利用堆這種資料結構的一種排序演算法,是選擇排序的一種。 如果是非降序排序,需要使用大根堆(是完全二叉樹),即每個節點的值都不大於父節點的值。將二叉樹的節點按照層級順序放入陣列,用長度為N+1的私有pq[]來表示一個大小為N的堆(為後面計數方便,不使用pq[0],堆元素放在pq[1]至pq[N]之間),根節點在位置1,它的子節點在位置2、3,以此類推。位置k的節點的兩個子節點位置分別為2k和2k+1,它的父節點為k/2。

堆的有序化:

  • 由下至上的堆有序化(上浮):如果堆的有序狀態因為某個節點變得比它的父節點更大而被打破,就需要交換它和它的父節點來修復堆,以此類推,直到遇到一個更大的父節點。
private void swim(int k){
	while(k > 1 && pq[k] > pq[k / 2]){//k位置的節點比父節點大則交換,注意k位置的節點不能是根節點
		int temp = pq[k / 2];
		pq[k / 2] = pq[k];
		pq[k] = temp;
		k /= 2;
	}
}
  • 由上至下的堆有序化(下沉):如果堆的有序狀態因為某個結點變得比它的兩個子節點或者其中之一更小而被打破,需要將它和它的兩個子節點中的較大者交換來恢復堆,以此類推,直到它的子節點都比它更小或者達到了堆的底部。
private void sink(int k){
	while(2 * k <= N){//k至少有子節點才能下沉
		int j = 2 * k;//左子節點
		if(j < N && pq[j] < pq[j + 1])
			j++;//找到子節點中較大的那個
		if(pq[k] >= pq[j])
			break;//k所在節點不小於子節點時結束下沉
		int temp = pq[k];
		pq[k] = pq[j];
		pq[j] = temp;
		k = j;
	}
}

堆排序分為兩個階段:

  • 堆的構造階段,將原始陣列重新組織安排進一個堆中;
  • 下沉排序階段,從堆中按遞減順序取出所有元素並得到排序結果。

從左至右遍歷陣列,用swim()保證下標左側的所有元素已經是一顆堆有序的完全樹即可,更高效的辦法是從右至左用sink()構造子堆。 時間複雜度為O(NlogN),不穩定的排序演算法。 Java實現程式碼如下:

	private static void exchange(int[] a, int i, int j){
		int temp = a[i];
		a[i] = a[j];
		a[j] = temp;
	}
	
	private static void sink(int[] a, int k, int N){
		while(2 * k <= N){
			int i = 2 * k;
			if(i < N && a[i] < a[i+1]){
				i++;
			}
			if(a[k] >= a[i]){
				break;
			}
			exchange(a, k, i);
			k = i;
		}
	}
	
	public static void sort(int[] a){
		int N = a.length - 1;
		for(int k = N / 2; k > 0; k--){
			sink(a, k, N);//從最後一個子堆開始構造有序堆
		}
		while(N > 1){
			exchange(a, 1, N--);//將最大的元素a[1]和a[N]交換
			sink(a, 1, N);//修復堆:將交換上來的a[1]下沉
		}
	}