排序算法——堆排

堆排序

如果將堆結構中的數據看作是一顆完全二叉樹的各個節點,那麼堆的性質就是滿足如下性質的完全二叉樹:樹中任意非葉子節點的關鍵碼均不大於或不小於其左右孩子節點的關鍵碼。利用堆的有序性及其運算,可以容易的實現選擇排序的方法稱爲堆排序。

假設欲對含有n個對象的序列v[0]、v[1]、v[n-1]進行堆排序,算法主要分爲兩個步驟:一是根據初始輸入數據利用堆的調整算法形成初始堆,二是通過一系列的對象交換和重新調整對堆進行排序。具體的過程是在首次獲得最大堆之後,將關鍵字最大的元素v[0](即堆頂)和無序區的最後一個元素v[n-1]進行交換。由此得到新的無序區v[0]、v[1]、v[n-2]和有序區v[n-1],且滿足v[0…n-2]中元素的關鍵碼不大於v[n-1]的關鍵碼。由於交換後新的堆頂v[0]可能違法堆的性質,因此再利用堆的調整算法對無序區v[0…n-2]進行調整。調整完成後將關鍵碼最大的元素v[0]和無序區的最後一個元素v[n-2]交換,並再次利用堆的調整算法對無序區進行調整,這個過程直到無序區中僅剩一個元素爲止。

/*堆排是從底向上的調整方式
最後一個父親節點的位置是 N / 2 - 1(數組是從0開始的)
假設數組中有10個元素
將最大的元素放在數組的最後
這時候樹中需要排序的數量減1,剩下9個元素
在這9個元素中,只有剛纔頂部交換的元素不符合大頂堆,其他元素都符合大頂堆
再對現在的頂部元素的位置調用剛纔的函數接口adjust_max_heap
該函數接口運行一次只調整一個節點。
調整後,再將該元素和數組中的倒數第二個元素進行交換,數組的len會不斷變小
堆排的所有時間複雜度都是nlog2n*/
void adjust_Max_Heap(int* arr, int adjustPos, int arrLen)
{
	int dad = adjustPos;   //adjustPos是要調整的節點位置
	int son = 2 * dad + 1;
	while (son < arrLen)    //arrLen代表數組的長度
	{
		if (son + 1 < arrLen && arr[son] < arr[son + 1]) //比較左孩子和右孩子,如果右孩子大於左孩子,將son加1,從而下一步拿右孩子與父親比較
		{
			son++;
		}
		if (arr[dad] < arr[son])
		{
			SWAP(arr[dad], arr[son]);
			dad = son;
			son = 2 * dad + 1;
		}
		else
		{
			break;
		}
	}
}

void arr_Heap(int* arr)
{
	int i;
	//將數組調整爲大根堆
	for (i = N / 2 - 1; i >= 0; i--) //從數組中最後一個dad節點進行調整
	{
		adjust_Max_Heap(arr, i, N);
	}
	SWAP(arr[0], arr[N - 1]);//交換頂部與最後一個元素
	for (i = N - 1; i > 1; i--)
	{
		adjust_Max_Heap(arr, 0, i); //將剩餘9個元素再次調整爲大根堆,不斷交換根部元素與數組尾部元素,然後調整的堆元素個數減一
		SWAP(arr[0], arr[i - 1]);//交換頂部元素與堆中最後一個元素,已經在尾部排好的不算堆中元素
	}
}

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