国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼)

這篇具有很好參考價(jià)值的文章主要介紹了數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼)。希望對大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃

做人要謙虛,多聽聽別人的意見,然后記錄下來,看看誰對你有意見

一、二叉樹的順序(堆)結(jié)構(gòu)及實(shí)現(xiàn)

1.二叉樹的順序結(jié)構(gòu)

2.堆的概念及結(jié)構(gòu)

3.堆的實(shí)現(xiàn)

3.1 向下調(diào)整算法 AdJustDown

3.2 向上調(diào)整算法 AdJustUP

3.3 堆的創(chuàng)建

3.3.1 向上建堆
3.3.2 向下建堆
3.3.3 堆的初始化與銷毀
3.3.4 堆的插入(壓棧)
3.3.5 取堆頂?shù)臄?shù)據(jù)
3.3.6 堆的刪除
3.3.7 堆的數(shù)據(jù)個(gè)數(shù)
3.3.8 堆的判空

二、堆的完整實(shí)現(xiàn)代碼

三、完結(jié)撒?

–?–?–?–?–?–?–?–?–?–?–?–?–?–?–?-正文開始-?–?–?–?–?–?–?–?–?–?–?–?–?–?–?–

一、二叉樹的順序(堆)結(jié)構(gòu)及實(shí)現(xiàn)

1.二叉樹的順序結(jié)構(gòu)


物理結(jié)構(gòu):數(shù)組
邏輯結(jié)構(gòu):二叉樹

順序結(jié)構(gòu)存儲就是使用數(shù)組來存儲,普通的二叉樹是不適合用數(shù)組來存儲的,因?yàn)榭赡軙嬖诖罅康目臻g浪費(fèi),而完全二叉樹更適合使用順序結(jié)構(gòu)存儲

可能有些同學(xué)不太清楚普通二叉樹使用數(shù)組來存儲為什么會造成空間上的浪費(fèi),這里給大家講解一下:
我們使用數(shù)組進(jìn)行二叉樹的存儲時(shí),父子節(jié)點(diǎn)之間需要滿足的關(guān)系為

parent = (child+1)/2;
leftchild = parent2-1;
rightchild = parent
2+2;
ps:parent指父節(jié)點(diǎn)在數(shù)組中的下標(biāo)位置,leftchild指左子節(jié)點(diǎn)在數(shù)組中的下標(biāo)位置,rightchild指右子節(jié)點(diǎn)在數(shù)組中的下標(biāo)位置

那么對于滿二叉樹和完全二叉樹,我們按照一層一層(層序)往數(shù)組中進(jìn)行存儲。
舉例如下圖所示,大家可以按照下圖簡單計(jì)算驗(yàn)證一下父子之間是否滿足父子關(guān)系:
數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃

可以知道,對于滿二叉樹和完全二叉樹進(jìn)行層序存儲在數(shù)組中,按照下標(biāo)計(jì)算都是滿足上面所述的父子關(guān)系。

而對于普通二叉樹(非滿二叉樹,非完全二叉樹),我們依然按照上面存儲進(jìn)行計(jì)算
數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃
可以發(fā)現(xiàn)不符合父子節(jié)點(diǎn)之間的關(guān)系,問題在于2節(jié)點(diǎn)的右節(jié)點(diǎn)為空,而在存儲時(shí)對于空節(jié)點(diǎn)我們并沒有在數(shù)組中進(jìn)行存儲記錄,相當(dāng)于在數(shù)組中少了一個(gè)位置,那么我們?nèi)绻芽展?jié)點(diǎn)加上,如下圖:
數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃
這樣就滿足了父子間的關(guān)系,但是對于下標(biāo)為4的位置并沒有存儲數(shù)據(jù),就造成了空間浪費(fèi)。

所以,對于普通二叉樹我們一般使用鏈?zhǔn)浇Y(jié)構(gòu)進(jìn)行存儲,避免空間浪費(fèi)。

現(xiàn)實(shí)中我們通常把堆(一種二叉樹)使用順序結(jié)構(gòu)的數(shù)組來存儲,需要注意的是這里的堆和操作系統(tǒng)虛擬進(jìn)程地址空間中的堆是兩回事,一個(gè)是數(shù)據(jù)結(jié)構(gòu),一個(gè)是操作系統(tǒng)中管理內(nèi)存的一塊區(qū)域分段。

2.堆的概念及結(jié)構(gòu)

如果有一個(gè)關(guān)鍵碼的集合K = {K0,K1,K2,…,Kn-1},把它的所有元素按完全二叉樹的順序存儲方式存儲在一個(gè)一維數(shù)組中,并滿足:Ki<=K2i+1且Ki<=K2i+2(Ki>=K2i+1且Ki>=K2i+2)i = 0,1,2…,則稱為小堆(或大堆)。將根節(jié)點(diǎn)最大的堆叫做最大堆或大根堆,根節(jié)點(diǎn)最小的堆叫做最小堆或小根堆。

堆的性質(zhì):

1.堆中某個(gè)節(jié)點(diǎn)的值總是不大于或不小于其父節(jié)點(diǎn)的值;
2.堆總是一棵完全二叉樹;

數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃
堆的兄弟節(jié)點(diǎn)(同一父節(jié)點(diǎn)的子兩個(gè)子節(jié)點(diǎn))之間是不論大小的,而對于小堆其父節(jié)點(diǎn)的值一定小于子節(jié)點(diǎn),對于大堆其父節(jié)點(diǎn)的值一定大于子節(jié)點(diǎn)。

3.堆的實(shí)現(xiàn)

堆初始化結(jié)構(gòu):

//堆初始化
void HPInit(HP* php)
{
	assert(php);

	php->a = NULL;
	php->capacity = php->size = 0;
}

在給定一個(gè)數(shù)組去實(shí)現(xiàn)成堆之前我們需要先了解內(nèi)部實(shí)現(xiàn)的核心邏輯。

3.1 向下調(diào)整算法 AdJustDown

現(xiàn)在我們給出一個(gè)數(shù)組,邏輯上看做一顆完全二叉樹。

int arr[] = {27,15,19,18,28,34,65,49,25,37}

我們通過從根節(jié)點(diǎn)開始的向下調(diào)整算法可以把它調(diào)整成一個(gè)小堆
向下調(diào)整算法有一個(gè)前提:左右子樹必須是一個(gè)堆,才能調(diào)整。
上面數(shù)組邏輯上的二叉樹可畫為:
數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃
可以看出來,根節(jié)點(diǎn)27影響了整體的小堆結(jié)構(gòu),那么我們?nèi)绾螌⑵滢D(zhuǎn)變?yōu)樾《涯兀?br>數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃

(制作不是很好大家將就著看看就行)
按照上面圖片顯示的流程,我們在邏輯上就把之前的二叉樹變成了小堆排序,而其邏輯實(shí)現(xiàn)思想就是向下調(diào)整。

向下調(diào)整

再強(qiáng)調(diào)一邊:
向下調(diào)整算法有一個(gè)前提:左右子樹必須是一個(gè)堆,才能調(diào)整。

從根開始,比較其子節(jié)點(diǎn)的大小,如果為大堆排序就將父節(jié)點(diǎn)與子節(jié)點(diǎn)中較大的節(jié)點(diǎn)交換,如果為小堆就將父節(jié)點(diǎn)與子節(jié)點(diǎn)中較小的節(jié)點(diǎn)交換。交換完畢繼續(xù)重復(fù)以上邏輯,直到父節(jié)點(diǎn)大于子節(jié)點(diǎn)(大堆)或是父節(jié)點(diǎn)小于子節(jié)點(diǎn)(小堆)即可完成堆排序。

代碼實(shí)現(xiàn):

void Swap(HPDataType* px, HPDataType* py)
{
	HPDataType tmp = *px;
	*px = *py;
	*py = tmp;
}

//向下調(diào)整O(logN)
void AdJustDown(HPDataType* a, int n, int parent)
{
	//從左孩子開始,child為小孩子那個(gè)
	 int child = parent * 2 + 1;

	 while (child<n)
	 {
		 
		 if (child + 1 < n && a[child] > a[child + 1])
		 {
			++child;
		 }

		 if (a[child] < a[parent])//小堆<,大堆>
		 {
			 Swap(&a[parent], &a[child]);
			 parent = child;
			 child = child * 2 + 1;
		 }
		 else
		 {
			 break;
		 }
	 }
}

3.2 向上調(diào)整算法 AdJustUP

在一個(gè)二叉樹的數(shù)組中如果我們尾插了一個(gè)數(shù)據(jù),可能就導(dǎo)致結(jié)構(gòu)不再是堆。

所以我們?nèi)绻窃诂F(xiàn)有的一個(gè)堆里進(jìn)行數(shù)據(jù)尾插存儲,那么我們要保證數(shù)據(jù)插入后還是堆,這時(shí)一般使用向上調(diào)整算法。

下面我們給出一個(gè)數(shù)組,請畫出其邏輯結(jié)構(gòu)二叉樹:

int arr[] = {10,15,56,25,30,70}

二叉樹:

數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃
如果我將5尾插在數(shù)組當(dāng)中,那么就相當(dāng)于是將56節(jié)點(diǎn)的右孩子連了一個(gè)5節(jié)點(diǎn):
數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃
這顯然已經(jīng)不是小堆了,調(diào)整邏輯如下:
數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃
這就是向上調(diào)整的整體邏輯:

如果是進(jìn)行小堆排序,將尾節(jié)點(diǎn)值與其父節(jié)點(diǎn)的值進(jìn)行比較,如果小于父節(jié)點(diǎn)就交換,如果進(jìn)行大堆,那就判斷子節(jié)點(diǎn)是否大于父節(jié)點(diǎn),若是大于就交換。

代碼實(shí)現(xiàn):

void Swap(HPDataType* px, HPDataType* py)
{
	HPDataType tmp = *px;
	*px = *py;
	*py = tmp;
}

//向上調(diào)整 O(logN)
void AdJustUP(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;

	//while (1)嚴(yán)格來說不行
	while(child>0)
	{
		if (a[child] < a[parent])//小堆<,大堆>
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

向下調(diào)整算法和向上調(diào)整算法的時(shí)間復(fù)雜度都為O(logN),大家感興趣可以算一下。

3.3 堆的創(chuàng)建

向上調(diào)整和向下調(diào)整都是基于已經(jīng)形成了堆上面,那么如果隨便給一個(gè)本就不是堆的數(shù)組,我們該如何進(jìn)行建堆呢?

3.3.1 向上建堆

下面我們給出一個(gè)數(shù)組,這個(gè)數(shù)組邏輯上可以看做一顆完全二叉樹,但是還不是一個(gè)小堆,現(xiàn)在我們通過算法,把它構(gòu)建成一個(gè)小堆。

int a[] = {5,3,6,8,2,1};

根節(jié)點(diǎn)左右子樹不是小堆,我們怎么調(diào)整呢?

這里我們從根的左孩子節(jié)點(diǎn)開始向上調(diào)整,根據(jù)數(shù)組存儲順序向后以此執(zhí)行,直到最后一個(gè)節(jié)點(diǎn)為止。

其二叉樹為:
數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃
調(diào)整邏輯:
數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃
從根節(jié)點(diǎn)的子節(jié)點(diǎn)開始,進(jìn)行向上調(diào)整,一次調(diào)整完畢將子節(jié)點(diǎn)對應(yīng)數(shù)組下標(biāo)加1進(jìn)入下一個(gè)節(jié)點(diǎn)進(jìn)行向上調(diào)整,直到除了根的所有節(jié)點(diǎn)都調(diào)整完畢,二叉樹便變成了堆。

代碼實(shí)現(xiàn):

//向上調(diào)整 O(logN)
void AdJustUP(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;

	//while (1)嚴(yán)格來說不行
	while(child>0)
	{
		if (a[child] < a[parent])//小堆<,大堆>
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//初始化建堆
void HPInitArray(HP* php, HPDataType* a, int n)
{
	assert(php);

	php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	memcpy(php->a, a, sizeof(HPDataType) * n);
	php->size = php->capacity = n;

	//向上建堆 O(N*logN)
	for (int i = 1; i < php->size; i++)
	{
		AdjustUp(php->a, i);
	}
}
3.3.2 向下建堆

向下建堆就是根據(jù)向下調(diào)整的邏輯進(jìn)行。

我們把二叉樹分為其根和子樹,再把子樹分為其根和子樹,將每一個(gè)分好的子樹都進(jìn)行向下調(diào)整,直到葉子節(jié)點(diǎn)為止。

我們還拿上面數(shù)組為例:

int a[] = {5,3,6,82,1};

這里我們從倒數(shù)的第一個(gè)非葉子節(jié)點(diǎn)的子樹開始調(diào)整,一直調(diào)整到根節(jié)點(diǎn)的樹,就可以調(diào)整成小堆

調(diào)整邏輯:數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃
代碼實(shí)現(xiàn):

//向下調(diào)整O(logN)
void AdJustDown(HPDataType* a, int n, int parent)
{
	//從左孩子開始,child為小孩子那個(gè)
	 int child = parent * 2 + 1;

	 while (child<n)
	 {
		 
		 //假設(shè)法選出左右節(jié)點(diǎn)中大/小的節(jié)點(diǎn)
		 if (child + 1 < n && a[child] > a[child + 1])
		 {
			++child;
		 }

		 if (a[child] < a[parent])//小堆<,大堆>
		 {
			 Swap(&a[parent], &a[child]);
			 parent = child;
			 child = child * 2 + 1;
		 }
		 else
		 {
			 break;
		 }
	 }
}

//初始化建堆
void HPInitArray(HP* php, HPDataType* a, int n)
{
	assert(php);

	php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	memcpy(php->a, a, sizeof(HPDataType) * n);
	php->size = php->capacity = n;
		//向下建堆 O(N)
	for (int i = (php->size - 1 - 1) / 2; i >= 0; i--)
	{
		AdJustDown(php->a, php->size, i);
	}
}

向上建堆和向下建堆的時(shí)間復(fù)雜度分別為O(N*logN),O(N)。
因?yàn)橄蛳陆ǘ训臅r(shí)間復(fù)雜度小,所以我們在實(shí)際工作中進(jìn)行建堆一般是選擇向下建堆

3.3.3 堆的初始化與銷毀

在數(shù)據(jù)結(jié)構(gòu)中,創(chuàng)建任何結(jié)構(gòu),都需要對其進(jìn)行初始化和銷毀。

代碼實(shí)現(xiàn):

//堆初始化
void HPInit(HP* php)
{
	assert(php);

	php->a = NULL;
	php->capacity = php->size = 0;
}

//堆銷毀
void HPDestory(HP* php)
{
	assert(php);

	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}
3.3.4 堆的插入(壓棧)

插入一個(gè)數(shù)到數(shù)組的尾上,再進(jìn)行向上調(diào)整算法,直到滿足堆。
代碼實(shí)現(xiàn):

//向上調(diào)整 O(logN)
void AdJustUP(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;

	//while (1)嚴(yán)格來說不行
	while(child>0)
	{
		if (a[child] < a[parent])//小堆<,大堆>
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//壓棧 O(logN)
void HPPush(HP* php, HPDataType x)
{
	assert(php);

	//判斷空間是否足夠
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, newcapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;

	//數(shù)據(jù)尾插向上調(diào)整
	AdJustUP(php->a, php->size-1);
}

3.3.5 取堆頂?shù)臄?shù)據(jù)

在建好的堆中返回其根部數(shù)據(jù)。
代碼實(shí)現(xiàn):

//返回根數(shù)據(jù)
HPDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size);

	return php->a[0];
}

3.3.6 堆的刪除

刪除堆是刪除堆頂?shù)臄?shù)據(jù),將堆頂?shù)臄?shù)據(jù)根最后一個(gè)數(shù)據(jù)一換,然后刪除數(shù)組最后一個(gè)數(shù)據(jù),再進(jìn)行向下調(diào)整算法。

代碼實(shí)現(xiàn):

//向下調(diào)整O(logN)
void AdJustDown(HPDataType* a, int n, int parent)
{
	//從左孩子開始,child為小孩子那個(gè)
	 int child = parent * 2 + 1;

	 while (child<n)
	 {
		 
		 if (child + 1 < n && a[child] > a[child + 1])
		 {
			++child;
		 }

		 if (a[child] < a[parent])//小堆<,大堆>
		 {
			 Swap(&a[parent], &a[child]);
			 parent = child;
			 child = child * 2 + 1;
		 }
		 else
		 {
			 break;
		 }
	 }
}

//刪除根數(shù)據(jù)O(logN)
void HPPop(HP* php)
{
	assert(php);
	assert(php->size);

	//將根數(shù)據(jù)與最后一個(gè)子葉交換,再刪除最后一個(gè)數(shù)據(jù)
	Swap(&php->a[0], &php->a[php->size-1]);
	php->size--;

	//向下調(diào)整
	AdJustDown(php->a, php->size, 0);
}
3.3.7 堆的數(shù)據(jù)個(gè)數(shù)

代碼實(shí)現(xiàn):

int HeapSize(HP* php)
{
	assert(php);

	return php->size;
}
3.3.8 堆的判空

代碼實(shí)現(xiàn):

//判斷堆是否為空
bool HPEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

二、堆的完整實(shí)現(xiàn)代碼

Heap.h:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>

typedef int HPDataType;

typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

//小堆

//堆初始化
void HPInit(HP* php);
//初始化建堆
void HPInitArray(HP* php, HPDataType* a, int n);

//堆銷毀
void HPDestory(HP* php);

//壓棧
void HPPush(HP* php, HPDataType x);

//返回根數(shù)據(jù)
HPDataType HPTop(HP* php);

//刪除根數(shù)據(jù)
void HPPop(HP* php);

//堆的數(shù)據(jù)個(gè)數(shù)
int HPSize(HP* php);

//判斷堆是否為空
bool HPEmpty(HP* php);

Heap.c:

#include "Heap.h"

//堆初始化
void HPInit(HP* php)
{
	assert(php);

	php->a = NULL;
	php->capacity = php->size = 0;
}

//堆銷毀
void HPDestory(HP* php)
{
	assert(php);

	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
}

void Swap(HPDataType* px, HPDataType* py)
{
	HPDataType tmp = *px;
	*px = *py;
	*py = tmp;
}

//向上調(diào)整 O(logN)
void AdJustUP(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;

	//while (1)嚴(yán)格來說不行
	while(child>0)
	{
		if (a[child] < a[parent])//小堆<,大堆>
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (parent - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

//壓棧 O(logN)
void HPPush(HP* php, HPDataType x)
{
	assert(php);

	//判斷空間是否足夠
	if (php->size == php->capacity)
	{
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = (HPDataType*)realloc(php->a, newcapacity * sizeof(HPDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}

		php->a = tmp;
		php->capacity = newcapacity;
	}

	php->a[php->size] = x;
	php->size++;

	//數(shù)據(jù)尾插向上調(diào)整
	AdJustUP(php->a, php->size-1);
}

//返回根數(shù)據(jù)
HPDataType HPTop(HP* php)
{
	assert(php);
	assert(php->size);

	return php->a[0];
}

//向下調(diào)整O(logN)
void AdJustDown(HPDataType* a, int n, int parent)
{
	//從左孩子開始,child為小孩子那個(gè)
	int child = parent * 2 + 1;

	while (child < n)
	{

		if (child + 1 < n && a[child] > a[child + 1])
		{
			++child;
		}

		if (a[child] < a[parent])//小堆<,大堆>
		{
			Swap(&a[parent], &a[child]);
			parent = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

//刪除根數(shù)據(jù)O(logN)
void HPPop(HP* php)
{
	assert(php);
	assert(php->size);

	//將根數(shù)據(jù)與最后一個(gè)子葉交換,再刪除最后一個(gè)數(shù)據(jù)
	Swap(&php->a[0], &php->a[php->size-1]);
	php->size--;

	//向下調(diào)整
	AdJustDown(php->a, php->size, 0);
}

int HeapSize(HP* php)
{
	assert(php);

	return php->size;
}

//判斷堆是否為空
bool HPEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

//初始化建堆
void HPInitArray(HP* php, HPDataType* a, int n)
{
	assert(php);

	php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	memcpy(php->a, a, sizeof(HPDataType) * n);
	php->size = php->capacity = n;

	//向上建堆 O(N*logN)
	/*for (int i = 1; i < php->size; i++)
	{
		AdjustUp(php->a, i);
	}*/

	//向下建堆 O(N)
	for (int i = (php->size - 1 - 1) / 2; i >= 0; i--)
	{
		AdJustDown(php->a, php->size, i);
	}
}

//建堆排序

//排序
// 升序
//小堆時(shí)間復(fù)雜度太大O(N^2),用大堆進(jìn)行排序O(N*logN)
//大堆

//升序  大堆 O(N*logN)
//降序  小堆 O(N*logN)
void HeapSort(HPDataType* a, int n)
{
	//根據(jù)數(shù)組直接建堆 O(N)
	for (int i = (n - 1 - 1) / 2; i >= 0; --i)
	{
		AdJustDown(a, n, i);
	}

	//交換根和尾的位置,刪除尾,再向上調(diào)整 O(N*logN)
	int end = n - 1;
	while (end > 0)
	{
		Swap(&a[0], &a[end]);
		AdJustDown(a, end, 0);
		--end;
	}
}

三、完結(jié)撒?

如果以上內(nèi)容對你有幫助不妨點(diǎn)贊支持一下,以后還會分享更多編程知識,我們一起進(jìn)步。
最后我想講的是,據(jù)說點(diǎn)贊的都能找到漂亮女朋友?
數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼),數(shù)據(jù)結(jié)構(gòu),算法,經(jīng)驗(yàn)分享,筆記,leetcode,鏈表,動態(tài)規(guī)劃文章來源地址http://www.zghlxwxcb.cn/news/detail-854123.html

到了這里,關(guān)于數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹順序存儲(堆)】的整體實(shí)現(xiàn)講解(賦完整實(shí)現(xiàn)代碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹鏈序存儲】的整體實(shí)現(xiàn)講解

    數(shù)據(jù)結(jié)構(gòu)進(jìn)階篇 之 【二叉樹鏈序存儲】的整體實(shí)現(xiàn)講解

    封建迷信我嗤之以鼻,財(cái)神殿前我長跪不起 1.1 手動創(chuàng)建 1.2 前序遞歸創(chuàng)建 2.1 前序,中序以及后序遍歷概念 2.2 層序遍歷概念 2.3 前序打印實(shí)現(xiàn) 2.4 中序打印實(shí)現(xiàn) 2.4 后序打印實(shí)現(xiàn) 2.5 層序打印實(shí)現(xiàn) 2.6 判斷是否為完全二叉樹 3.1 二叉樹節(jié)點(diǎn)個(gè)數(shù) 3.2 二叉樹第k層節(jié)點(diǎn)個(gè)數(shù) 3.3 二叉樹

    2024年04月13日
    瀏覽(35)
  • 【數(shù)據(jù)結(jié)構(gòu)】二叉樹——順序結(jié)構(gòu)

    【數(shù)據(jù)結(jié)構(gòu)】二叉樹——順序結(jié)構(gòu)

    由于每個(gè)節(jié)點(diǎn)都 只有一個(gè)父節(jié)點(diǎn) ,所以我們可通過雙親來表示一棵樹。具體方式通過 數(shù)組的形式 實(shí)現(xiàn)。 根節(jié)點(diǎn)的下標(biāo)為0 按照層序從上到下排序 每層從左向右遞增 表示形式: 二維數(shù)組 數(shù)據(jù)的列標(biāo)為0 ,只需確定行標(biāo),即可鎖定位置 根節(jié)點(diǎn)的父節(jié)點(diǎn)下標(biāo)為 -1 列標(biāo)為1存父節(jié)

    2024年02月02日
    瀏覽(20)
  • 數(shù)據(jù)結(jié)構(gòu)-二叉樹·堆(順序結(jié)構(gòu)的實(shí)現(xiàn))

    數(shù)據(jù)結(jié)構(gòu)-二叉樹·堆(順序結(jié)構(gòu)的實(shí)現(xiàn))

    ??個(gè)人名片: ??作者簡介:一名樂于分享在學(xué)習(xí)道路上收獲的大二在校生 ????個(gè)人主頁??:GOTXX ??個(gè)人WeChat : ILXOXVJE ??本文由GOTXX原創(chuàng),首發(fā)CSDN?????? ??系列專欄:零基礎(chǔ)學(xué)習(xí)C語言----- 數(shù)據(jù)結(jié)構(gòu)的學(xué)習(xí)之路 ??每日一句:如果沒有特別幸運(yùn),那就請?zhí)貏e努力!??

    2024年02月05日
    瀏覽(14)
  • 數(shù)據(jù)結(jié)構(gòu):二叉樹的順序結(jié)構(gòu)--堆

    數(shù)據(jù)結(jié)構(gòu):二叉樹的順序結(jié)構(gòu)--堆

    朋友們、伙計(jì)們,我們又見面了,本期來給大家解讀一下二叉樹--堆的相關(guān)知識點(diǎn),如果看完之后對你有一定的啟發(fā),那么請留下你的三連,祝大家心想事成! C 語 言 專 欄:C語言:從入門到精通 數(shù)據(jù)結(jié)構(gòu)專欄:數(shù)據(jù)結(jié)構(gòu) 個(gè)? 人? 主? 頁 :stackY、 目錄 前言: 1.堆的概念及

    2024年02月06日
    瀏覽(28)
  • 【數(shù)據(jù)結(jié)構(gòu)】二叉樹的順序結(jié)構(gòu)-堆

    【數(shù)據(jù)結(jié)構(gòu)】二叉樹的順序結(jié)構(gòu)-堆

    普通的二叉樹是不適合用數(shù)組來存儲的,因?yàn)榭赡軙嬖诖罅康目臻g浪費(fèi)。而 完全二叉樹 更適合使用順序結(jié)構(gòu)存儲。 現(xiàn)實(shí)中我們通常把堆(一種二叉樹)使用順序結(jié)構(gòu)的數(shù)組來存儲,需要注意的是這里的堆和操作系統(tǒng)虛擬進(jìn)程地址空間中的堆是兩回事,一個(gè)是數(shù)據(jù)結(jié)構(gòu),一個(gè)

    2024年02月09日
    瀏覽(29)
  • 數(shù)據(jù)結(jié)構(gòu)——順序二叉樹——堆

    數(shù)據(jù)結(jié)構(gòu)——順序二叉樹——堆

    ? ? ? ? 在介紹二叉樹之前,我們首先要明確樹是什么。 ????????樹 用我們的通常認(rèn)識來判斷應(yīng)該是一種植物,從根向上生長,分出許多的樹枝并長出葉子。對于數(shù)據(jù)結(jié)構(gòu)中的樹而言,其結(jié)構(gòu)也正是從樹的特征中剝離出來的。樹結(jié)構(gòu)是一種非線性數(shù)據(jù)結(jié)構(gòu),具有一個(gè)根結(jié)

    2024年01月19日
    瀏覽(18)
  • 【數(shù)據(jù)結(jié)構(gòu)】二叉樹的順序結(jié)構(gòu)及實(shí)現(xiàn)

    【數(shù)據(jù)結(jié)構(gòu)】二叉樹的順序結(jié)構(gòu)及實(shí)現(xiàn)

    目錄 1. 二叉樹的順序結(jié)構(gòu) 2. 堆的概念及結(jié)構(gòu) 3. 堆的實(shí)現(xiàn) 3.1 堆向下調(diào)整算法 3.2 堆的創(chuàng)建 3.3 建堆時(shí)間復(fù)雜度 3.4 堆的插入 3.5 堆的刪除 3.6 堆的代碼實(shí)現(xiàn) 4. 堆的應(yīng)用 4.1 堆排序 4.2 TOP-K問題 普通的二叉樹是不適合用數(shù)組來存儲的,因?yàn)榭赡軙嬖诖罅康目臻g浪費(fèi)。而完全二叉

    2024年02月08日
    瀏覽(23)
  • 【數(shù)據(jù)結(jié)構(gòu)】樹、二叉樹的概念和二叉樹的順序結(jié)構(gòu)及實(shí)現(xiàn)

    【數(shù)據(jù)結(jié)構(gòu)】樹、二叉樹的概念和二叉樹的順序結(jié)構(gòu)及實(shí)現(xiàn)

    之前我們學(xué)習(xí)了順序表、鏈表以及棧和隊(duì)列這些數(shù)據(jù)結(jié)構(gòu),但這些數(shù)據(jù)結(jié)構(gòu)都是線性的(一對一)。接下來要學(xué)習(xí) 非線性的數(shù)據(jù)結(jié)構(gòu)——樹(二叉樹) ,相比前面的,樹的結(jié)構(gòu)更加復(fù)雜,話不多說,直接進(jìn)入正題吧。 樹是一種 非線性的數(shù)據(jù)結(jié)構(gòu) ,它是 一對多(也有可能是

    2024年02月07日
    瀏覽(27)
  • 數(shù)據(jù)結(jié)構(gòu)初階--二叉樹的順序結(jié)構(gòu)之堆

    數(shù)據(jù)結(jié)構(gòu)初階--二叉樹的順序結(jié)構(gòu)之堆

    目錄 一.堆的概念及結(jié)構(gòu) 1.1.堆的概念 1.2.堆的存儲結(jié)構(gòu) 二.堆的功能實(shí)現(xiàn) 2.1.堆的定義 2.2.堆的初始化 2.3.堆的銷毀 2.4.堆的打印 2.5.堆的插入 向上調(diào)整算法 堆的插入 2.6.堆的刪除 向下調(diào)整算法 堆的刪除 2.7.堆的取堆頂元素 2.8.堆的判空 2.9.堆的求堆的大小 三.堆的創(chuàng)建 3.1.向上調(diào)

    2024年02月14日
    瀏覽(27)
  • 初階數(shù)據(jù)結(jié)構(gòu)之---二叉樹的順序結(jié)構(gòu)-堆

    初階數(shù)據(jù)結(jié)構(gòu)之---二叉樹的順序結(jié)構(gòu)-堆

    今天要講的堆,不是操作系統(tǒng)虛擬進(jìn)程地址空間中(malloc,realloc等開空間的位置)的那個(gè)堆,而是數(shù)據(jù)結(jié)構(gòu)中的堆,它們雖然名字相同,卻是截然不同的兩個(gè)概念。堆的底層其實(shí)是 完全二叉樹 ,如果你問我,完全二叉樹是什么。好吧,那我先從樹開始講起,開始我們今天的

    2024年03月14日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包