堆排序 快速排序C實現

堆排序 

堆:堆是一棵完全二叉樹,它具有以下的性質,每個結點的值大於等於其左右孩子結點的值,稱爲大頂堆;或每個結點的值小於等於其左右孩子的值,稱爲小頂堆。

基本原理:首先需創建一個大頂堆(升序),將堆頂與數組最後一個元素交換,此時堆不再滿足堆的性質,因此我們再對剩下的n-1個元素進行調整,使其重新成爲一個大頂堆。再將堆頂元素與倒數第二個元素交換,交換之後又不滿足堆的性質,此時再對剩餘的n-2個元素進行調整,使其重新成爲一個大頂堆。依次類推......

那麼如何創建大頂堆呢?我們藉助一個調整函數,該函數的功能是在某個結點的左右子樹均爲大頂堆時,對以該結點爲根的這棵樹進行調整,使其成爲大頂堆。在創建時,實際上就是從完全二叉樹的最後一個非葉子結點開始向前調整,直到根節點調整完畢,此時大頂堆便創立起來了。

因爲堆是一顆完全二叉樹,因此可用一維數組來存儲。

調整函數具體調整過程如下圖,以0號元素(18)爲例,先判斷左子樹是否存在,若不存在則不需要調整,若存在則找出左孩子和右孩子(若右孩子不存在,則左孩子爲較大者)中的較大者,比較結點與孩子中的較大者,若結點不小於較大者,則調整結束;否則交換結點與左右孩子中的較大者,又因爲交換之後可能使子樹又不滿足堆的性質,則需繼續調整。

調整函數代碼:

void HeapAdjust(int arr[], int size, int parent)
{
	int child = 2 * parent + 1;
	while (child < size)
	{
		//找左右孩子中的較大者
		if (child + 1 < size && arr[child + 1] > arr[child])
			child += 1;
		//判斷是否需要交換
		if (arr[parent] < arr[child])
		{
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child = 2 * parent + 1;
		}
		else
			return;
	}
}

排序代碼:


void HeapSort(int arr[], int size)
{
	//end爲最後一個非葉子節點
	int end = (size - 2) / 2;
	//1創建堆
	for (; end >= 0; end--)
		HeapAdjust(arr, size, end);

	//2排序
	end = size - 1;  //end爲最後一個結點
	for (; end > 0; end--)
	{
		Swap(&arr[0], &arr[end]);
		HeapAdjust(arr, end, 0);
	}
}


快速排序

基本思想:通過一次排序將待排記錄分割成獨立的兩部分,其中一部分的記錄的關鍵字均比另一部分記錄的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。至於分割的方法則有多種。

方法1:

假設有序列 9, 4, 2, 6, 3, 1, 8, 7, 0, 5

我們選取最後一個元素作爲基準值(5),設有begin和end兩個指針分別指向第一個元素和最後一個元素,begin從左往右找第一個大於5的元素,此時begin指向9,end從右往左找第一個小於5的元素,end指向0,交換9和0此時變成

0, 4, 2, 6, 3, 1, 8, 7, 9, 5

繼續查找,第二次begin指向6,end指向1,交換之後

0, 4, 2, 1, 3, 6, 8, 7, 9, 5

第三次begin向後移動指向6,此時begin==end循環斷開,再將基準值與arr[begin]交換得到

0, 4, 2, 1, 3,      5,      8, 7, 9, 6

此時已將序列分成兩個序列,且左序列小於右序列。此時繼續對左右序列進行分割即可。

代碼如下:

//int Partion(int arr[], int left, int right)
//{
//	int key = arr[right];
//	int begin = left;
//	int end = right;
//	while (begin < end)
//	{
//		//從左往右找第一個大於key的數組下標
//		while (begin < end && arr[begin] <= key)
//			begin++;
//		//從右往左找第一個小於key的數組下標
//		while (begin < end && arr[end] >= key)   
//			end--;                                    
//		if (begin != end)
//		{
//			Swap(&arr[begin], &arr[end]);
//		}
//	}
//	Swap(&arr[begin], &arr[right]);
//	return begin;
//}


方法2:挖坑法

和方法1不同的是當從begin位置往右找到第一個大於基準值的元素時,直接用當前元素覆蓋end位置元素(當然需先記錄基準值),然後再從end位置往左找第一個小於基準值的元素來覆蓋begin位置元素。然後再從begin位置繼續按上述方法執行,直到begin==end,再用基準值覆蓋begin位置元素。至此,完成分割操作。

分割過程如下:

 

9, 4, 2, 6, 3, 1, 8, 7, 0, 5  

                               ←

9, 4, 2, 6, 3, 1, 8, 7, 0,

 

0, 4, 2, 6, 3, 1, 8, 7, 0, 9

                            ←

0, 4, 2, 6, 3, 1, 8, 7, 6, 9

 

0, 4, 2, 1, 3, 1, 8, 7, 6, 9


0, 4, 2, 1, 3, 5, 8, 7, 6, 9

代碼如下:

//int Partion(int arr[], int left, int right)
//{
//	int key = arr[right];//用最後一個元素做基準值
//	int begin = left;
//	int end = right;
//	while (begin < end)
//	{
//		//找第一個比基準值大的元素
//		while (begin < end && arr[begin] <= key)
//			begin++;
//		//將比基準值大的移到高端
//		arr[end] = arr[begin];
//		//找第一個比基準值小的的元素
//		while (begin < end && arr[end] >= key)
//			end--;
//		//將比基準值小的移到低端
//		arr[begin] = arr[end];  
//	}
//	arr[begin] = key;
//	return begin;
//}

整體代碼:

void QuickSort(int arr[], int left, int right)
{
	if (left < right)
	{
		int div = Partion(arr, left, right);
		QuickSort(arr, left, div - 1);
		QuickSort(arr, div + 1, right);
	}
}

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