1. 程式人生 > 其它 >【排序演算法】堆排序初探

【排序演算法】堆排序初探

【排序演算法】堆排序初探

定義

堆(heap)是一類特殊的資料結構的統稱。
堆通常是一個可以被看做一棵完全二叉樹的陣列物件。

About Binary Trees(二叉樹相關)

Binary Tree(二叉樹): 是樹的一種,主要的特點是二叉樹的所有節點最多隻有兩個葉節點。除此之外沒有別的要求。
Complete Binary Tree(完全二叉樹): 二叉樹的一種。在完全二叉樹當中,除了最後一層之外,所有層的節點都是滿的,且最後一層的節點也是從左到右的。優先填滿左邊的節點。

Full Binary Tree(滿二叉樹): 二叉樹的一種。滿二叉樹的所有層,包括最後一層,都是滿的。也就是說,除了最後一層的節點外所有的節點都有兩個子節點。

About Binary Heap(二叉堆相關)

Heap(堆): 堆是一種樹。在樹的性質之外,堆要求節點按照大小(父節點比子節點大/父節點比子節點小)來排列。
Binary Heap(二叉堆): 二叉堆是近似於完全二叉樹的一種堆。除完全二叉樹的性質外,二叉堆還要求堆內元素按照大小排列。
Min Heap(最小堆): 最小的鍵值總是在最前面。換句話說,所有的父節點都比他們的子節點小。
Max Heap(最大堆): 最大的鍵值總是在最前面。換句話說,所有的父節點都比他們的子節點大。

作者:匿名使用者
連結:https://www.zhihu.com/question/36134980/answer/1202061173
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

思想

堆排序(Heapsort)是指利用堆這種資料結構所設計的一種排序演算法。
堆的性質保證了其根元素為最大或最小值;
每一次建立大/小頂堆,在堆頂取出最大/小值,把當前剩餘的最後一個元素放到堆頂;
再次建立大/小頂堆,得到全部資料中第二大/小的值,取出,把當前剩餘的最後一個元素放到堆頂;
重複上述步驟,直到全部元素都被取出。

假設一個小根堆的左、右子樹都已是堆,並且根的元素名為root ,其左右子節點為left 和right ,這種情況下,有兩種可能:

(1) root<=left 並且 root<=right ,此時堆已完成;
(2)root>left 或者 root>right,此時 root 應與兩個子女中值較小的一個交換,結果得到一個堆,除非 root 仍然大於其新子女的一個或全部的兩個。這種情況下,我們只需簡單地繼續這種將 “拉下來”的過程,直至到達某一個層使它小於它的子女,或者它成了葉結點。

步驟

以升序為例,需要不斷建立大頂堆

  1. 初始化大頂堆 (從倒數第二行的節點開始,從下往上、從右往左,這樣遍歷的順序保證了一個小根堆的左、右子樹都已是堆)
    假設一個小根堆的左、右子樹都已是堆,並且根的元素名為root ,其左右子節點為left 和right ,這種情況下,有兩種可能:
    (1) root<=left 並且 root<=right ,此時堆已完成;
    (2)root>left 或者 root>right,此時 root 應與兩個子女中值較小的一個交換,結果得到一個堆,除非 root 仍然大於其新子女的一個或全部的兩個。這種情況下,我們只需簡單地繼續這種將 “拉下來”的過程,直至到達某一個層使它小於它的子女,或者它成了葉結點。
  2. 將陣列第一個元素(即根節點)與陣列最後一個元素交換(這時最後一個元素成為了最大的)
  3. 重塑大頂堆
  4. 重複2.3.步驟,直至只剩下根節點

程式碼實現

  1. Down——把i逐層向下比較,放到合適的地方;結合一定的呼叫前提,實現了把以i為堆頂的子堆變為大頂堆的功能。
void Down(int array[], int i, int size)
{
	int parent = i;
	int child = 2 * i + 1;
	while (child < size)
	{
		//比較兩個兒子的大小(如果有兩個兒子的前提下)
		//換成更大的那個去和父親比較
		if (child + 1 < size && array[child] < array[child + 1])
		{
			child++;
		}
		//如果父親不比兩個兒子都大
		//那麼交換父親和兒子
		//並且繼續往下檢視以剛剛的兒子為根的子堆
		if (array[parent] < array[child])
		{
			swap(array, parent, child);
			parent = child;
			child = 2 * child + 1;
		}
		//如果父親比兩個兒子都大,則大頂堆已經完成
		//因為我們遍歷的順序保證了一個子堆的左右子樹都已經是大頂堆
		else break;
	}
}
  1. BuildHeap——將原無序陣列初始化為一個大頂堆
void BuildHeap(int array[], int size)
{
	for (int i = size / 2 - 1; i >= 0; i--)
		Down(array, i, size);
}

比如,按照順序,先對倒數第二排的節點呼叫Down函式,結果是,倒數第二排結合最後一排成為了一個個大頂堆;
然後迴圈來到倒數第三排的節點,再呼叫Down(由上述知,已經滿足了每個子堆的左右子樹都已經是大頂堆的前提),之後倒數第三、二、一排共同組成了一個個大頂堆;
以此類推,最終構建出了一個真正的大頂堆。

  1. HeapSort——實現堆排序
void HeapSort(int array[], int size)
{
	//初始化一個大頂堆
	BuildHeap(array, size);
	//將堆頂元素和堆中剩餘的最後一個元素交換
	//再把交換後的堆頂放置到合適的地方
	//由於只改變了堆頂的元素,不會改變其他子堆已經是大頂堆的事實
	for (int i = size - 1; i > 0; i--)
	{
		swap(array, 0, i);
		Down(array, 0,i);
	}
}
  1. main
int main()
{
	int array[] = { 20, 19, 18, 30, 13, 30, 27, 15, 10 };
	int size = sizeof(array) / sizeof(int);
	HeapSort(array, size);
	for (int i = 0; i < size; i++)
		cout << array[i] << ' ';
}

輸出結果

在這裡插入圖片描述