看正月點燈籠老師的筆記—堆排序


視頻筆記:https://www.bilibili.com/video/BV1Eb41147dK?from=search&seid=7630499509795698118

 

一,大根堆

堆排序要用到 大根堆的數據結構

1,為完全二叉樹:添加新節點的順序是:從上到下,從左到右

2,滿足:父節點 > 子節點

 

二,heapify:堆調整

1,針對結點 i,將其兩個子節點找出來,此三個結點構成一個最小單位的完全二叉樹(越界的忽略)

2,找到這個最小單位的完全二叉樹 的最大值,並將其交換至父節點的位置

3,遞歸調用,維護交換后 子節點與其子結點被破壞的堆關系,遞歸出口為葉節點

4,結果,構造一個最小單位的完全二叉樹對應的堆

注意邊界的判定:上界:元素個數:n,在求孩子節點時,如果孩子結點的下標大於等於n,則不參與任何操作(指:不參與堆調整)。而且遞歸的遞歸出口是元素下標大於等於n,這是因為堆調整是自頂向下進行調整的,同樣因此,就不用考慮元素下標小於0的情況。

 

二,build_heap:構造堆

① 用一維數組表示:堆(因為堆的結構與完全二叉樹一樣)

  使用數組下標為 0 的位置,則 a[ i ] 的父節點為 a[ (i-1/)2 ],兩個子節點為 a[ i*2+1 ] 和 a[ i*2+2 ]

② 由於 heapify 會遞歸調用維護下面的堆,所以要構造完整的堆,要從下面往上構造:先找到最后一個父結點,再從最后一個父結點開始向上調用 heapify 

 

三,heap_sort:利用堆進行堆排序

由堆可以得到什么呢 ?首先根結點是最大的,第二層的兩個節點是 第二大和第三大的。

於是,利用這點就可以排序了

1,可以想到:既然根結點已經是最大的,那么就把這個數提走,剩下的再構造一個新堆。然后再提走根結點,再構造新的堆,循環往復。

2,但是這樣就會面臨一個問題?拿走根結點之后,根結點的數值由誰取代?

3,由於該結點要滿足:移動該節點到根結點后,要對堆的結構破壞盡可能小,而且堆的大小還要因為結點少了一個進行減1(即 數組表示堆的范圍要減 一)。顯然最后一個結點是最好的選擇了。移動后既不會影響到后面的結點,原來的位置還因為空了可以直接取消掉。

4,關鍵的 一步出現了。我們用最后一個結點替換根結點,此時根結點和第二層的兩個結點構成一個最小單位的最小二叉樹,此時就可以直接調用 heapify函數 ,形成一個新的堆。沒有必要用 build_heap函數,因為此時最大值就在第二層,根結點會因為遞歸維護被替換到最后一層,其他結點也會因為遞歸維護而得到調整。

5,至於拿出來的結點放哪,當然還是放在原來的數組里咯。由於數組前面存的是堆,於是可以從最后面的位置依次往前放,還可以形成升序序列。這也是為什么要用大根堆,因為默認我們想要的是升序序列,如果想要降序序列的話,也可以用小根堆。

 

四,代碼

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
void swap(int a[], int i, int j) // 交換結點位置
{
	int t = a[i];
	a[i] = a[j];
	a[j] = t;
}
void heapify(int tree[], int n, int i) // 堆調整
{
	if (i >= n)
		return;
	int c1 = 2 * i + 1;
	int c2 = 2 * i + 2;

	int max = i;
	if (c1 < n&&tree[c1] > tree[max])
		max = c1;
	if (c2 < n&&tree[c2] > tree[max])
		max = c2;
	if (max != i)
	{
		swap(tree, max, i); // 將最大值移至父節點
		heapify(tree, n, max); // 遞歸維護下面的結點
	}
}
void build_heap(int tree[], int n) // 構造堆
{
	int last_node = n - 1;
	int parent = (last_node - 1) / 2; // 最后一個父結點

	for (int i = parent; i >= 0; i--) // 從最后一個父結點開始向上調用 heapify
		heapify(tree, n, i);
}
void heap_sort(int tree[], int n) // 利用堆進行堆排序
{
	build_heap(tree, n); 
	for (int i = n - 1; i >= 0; i--) // 每次取出根結點后,堆減少一個節點
	{
		swap(tree, i, 0);
		heapify(tree, i, 0); // 構造一個新堆
	}
}
int main(void)
{
	int tree[] = { 2,5,3,1,10,4 };
	int n = 6;

	heap_sort(tree, n);
	for (int i = 0; i < n; i++)
	{
		printf("%d ", tree[i]);
	}puts("");

	system("pause");
	return 0;
}

  

over ! !  感謝觀看!!

========== ========= ========= ======= ====== ===== ==== === == =

寄揚州韓綽判官  杜牧 唐

青山隱隱水迢迢,秋盡江南草未凋。

二十四橋明月夜,玉人何處教吹簫。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM