堆排序

堆排序利用了大根堆(或小根堆)堆頂記錄的關鍵字最大(或最小)這一特徵,使得在當前無序區中選取最大(或最小)關鍵字的記錄變得簡單。
(1)用大根堆排序的基本思想
① 先將初始文件R[1..n]建成一個大根堆,此堆爲初始的無序區
② 再將關鍵字最大的記錄R[1](即堆頂)和無序區的最後一個記錄R[n]交換,由此得到新的無序區R[1..n-1]和有序區R[n],且滿足R[1..n-1].keys≤R[n].key
③由於交換後新的根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]調整爲堆。
……
直到無序區只有一個元素爲止。
(2)大根堆排序算法的基本操作:
①建堆,建堆是不斷調整堆的過程,從len/2處開始調整,一直到第一個節點,此處len是堆中元素的個數。建堆的過程是線性的過程,從len/2到0處一直調用調整堆的過程,相當於o(h1)+o(h2)…+o(hlen/2) 其中h表示節點的深度,len/2表示節點的個數,這是一個求和的過程,結果是線性的O(n)。
②調整堆:調整堆在構建堆的過程中會用到,而且在堆排序過程中也會用到。利用的思想是比較節點i和它的孩子節點left(i),right(i),選出三者最大(或者最小)者,如果最大(小)值不是節點i而是它的一個孩子節點,那邊交互節點i和該節點,然後再調用調整堆過程,這是一個遞歸的過程。調整堆的過程時間複雜度與堆的深度有關係,是lgn的操作,因爲是沿着深度方向進行調整的。
③堆排序:堆排序是利用上面的兩個過程來進行的。首先是根據元素構建堆。然後將堆的根節點取出(一般是與最後一個節點進行交換),將前面len-1個節點繼續進行堆調整的過程,然後再將根節點取出,這樣一直到所有節點都取出。堆排序過程的時間複雜度是O(nlgn)。因爲建堆的時間複雜度是O(n)(調用一次);調整堆的時間複雜度是lgn,調用了n-1次,所以堆排序的時間複雜度是O(nlgn)[2] 

注意

①只需做n-1趟排序,選出較大的n-1個關鍵字即可以使得文件遞增有序。
②用小根堆排序與利用大根堆類似,只不過其排序結果是遞減有序的。堆排序和直接選擇排序相反:在任何時刻堆排序中無序區總是在有序區之前,且有序區是在原向量的尾部由後往前逐步擴大至整個向量爲止

特點

堆排序(HeapSort)是一樹形選擇排序。堆排序的特點是:在排序過程中,將R[l..n]看成是一棵完全二叉樹順序存儲結構,利用完全二叉樹中雙親結點和孩子結點之間的內在關係(參見二叉樹的順序存儲結構),在當前無序區中選擇關鍵字最大(或最小)的記錄

平均性能

O(N*logN)。

其他性能

由於建初始堆所需的比較次數較多,所以堆排序不適宜於記錄數較少的文件。
堆排序是就地排序,輔助空間爲O(1).
它是不穩定的排序方法。(排序的穩定性是指如果在排序的序列中,存在前後相同的兩個元素的話,排序前 和排序後他們的相對位置不發生變化)
下邊是代碼:
void AdjustDown(int* a, int parent,int size)	//向下調整
{
	int child = parent * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && a[child] > a[child + 1])
		{
			child++;
		}
		if (a[child] < a[parent])
		{
			swap(a[child], a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
//建小堆
void BuildHeap(int* a,int size)
{
	for (int i = (size - 2) / 2; i >= 0; i--)
	{
		swap(a[0], a[i]);
		AdjustDown(a, i, size);
	}
}

void HeapSort(int* a, int size)
{
	BuildHeap(a, size);
	for (int i = size - 1; i >= 0; i--)
	{
		swap(a[0], a[i]);
		AdjustDown(a, 0, i);
	}
}

void Print(int* a, int size)
{
	for (int i = 0; i < size; i++)
	{
		cout << a[i] << " ";
	}
	cout << endl;
}

測試代碼
void Test2()
{
	int a[] = { 1, 0, 3, 5, 8, 2, 6, 7, 4, 9, 10 };
	int len = sizeof(a) / sizeof(a[0]);
	HeapSort(a, len);
	Print(a, len);
}
int main()
{
	//Test();
	Test2();
	return 0;
}


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