亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C語言數(shù)據(jù)結(jié)構(gòu)之堆、堆排序的分析及實現(xiàn)

 更新時間:2022年04月20日 10:26:06   作者:小白又菜  
堆是一個近似完全二叉樹的結(jié)構(gòu),并同時滿足堆積的性質(zhì),下面這篇文章主要給大家介紹了關(guān)于C語言數(shù)據(jù)結(jié)構(gòu)之堆、堆排序的分析及實現(xiàn)的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下

 1.堆的概念結(jié)構(gòu)及分類

以上這段概念描述看起來十分復(fù)雜,晦澀難懂。那么堆用通俗語言簡單描述如下:

堆是一個完全二叉樹的順序存儲。在一個堆中,堆的父節(jié)點一定大于等于(或小于等于)子節(jié)點。一旦有一部分不滿足則不為堆。

堆的性質(zhì): 

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

1.2堆的分類

1.2.1 大堆

在一個堆中,父節(jié)點一定大于等于子節(jié)點的堆稱為大堆。又稱大根堆。

1.2.2 小堆

在一個堆中,父節(jié)點一定小于等于子節(jié)點的堆稱為小堆。又稱小根堆。(下圖就是一個小堆)

習(xí)題練習(xí):

1.下列關(guān)鍵字序列為堆的是:(A)
A 100,60,70,50,32,65
B 60,70,65,50,32,100
C 65,100,70,32,50,60
D 70,65,100,32,50,60
E 32,50,100,70,65,60
F 50,100,70,65,60,32

分析:選項A分析后為大堆,其他選項多多少少都存在錯誤。(畫圖分析如下)

2. 堆的主要接口

在本篇文章中我們主要以小堆為例實現(xiàn)。

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

其中堆中包括以下主要功能:

1.堆的初始化   2.堆銷毀  3.堆打印  4.堆的插入元素  5.堆刪除元素   6.判斷堆是否為空  7.求堆中元素的個數(shù)  8.求堆頂元素

詳細(xì)接口如下:

//小堆
//算法邏輯思想是二叉樹,物理上操作的是數(shù)組中數(shù)據(jù)
typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;   //數(shù)組a
	size_t size;     //下標(biāo)
	size_t capacity; //容量
}HP;
 
void Swap(HPDataType* pa, HPDataType* pb);//交換函數(shù)
void HeapInit(HP* php);//堆初始化
void HeapDestory(HP* php);//堆銷毀
void HeapPrint(HP* php);//堆打印
 
//插入x以后,仍然要保證堆是(大/小)堆
void HeapPush(HP* php, HPDataType x);
 
//刪除堆頂?shù)臄?shù)據(jù)(最大/最?。?
void HeapPop(HP* php);
 
bool HeapEmpty(HP* php); //判斷是否為空
size_t HeapSize(HP* php);//求元素個數(shù)
HPDataType HeapTop(HP* php);//求堆頂元素

3.堆的實現(xiàn)

有了如上的接口,接下來我們實現(xiàn)各個接口。由于我們使用數(shù)組來實現(xiàn)堆,大多接口功能和順序表的實現(xiàn)相同。相同的實現(xiàn)這里不再過多分析。

3.1 堆的初始化 HeapInit

void HeapInit(HP* php)
{
	assert(php);
	php->a = NULL;
	php->size = php->capacity = 0;
 
}

3.2 堆的銷毀 HeapDestory

void HeapDestory(HP* php)
{
	assert(php);
	free(php->a);
	php->a = NULL;
	php->capacity = php->size = 0;
 
}

3.3 堆的打印 HeapPrint

void HeapPrint(HP* php)
{
	assert(php);
	for (size_t i = 0; i < php->size; ++i)
	{
		printf("%d ", php->a[i]);
	}
	printf("\n");
}

3.4 堆的插入元素 HeapPush   *

堆的元素插入是堆的一大重點和難點。接下來我們對該功能進(jìn)行分析和實現(xiàn)。

功能分析:

1、我們要向堆中插入元素,我們首先要判斷數(shù)組是否空間已滿,如果空間已滿就要擴(kuò)容。擴(kuò)容后再將新元素插入數(shù)組尾部。此過程和順序表相同。

2、由于插入新元素,我們要對該元素進(jìn)行分析(此處以如下圖小堆舉例),分析插入元素是否會破壞堆結(jié)構(gòu),如果破壞了堆,我們就要對堆進(jìn)行向上調(diào)整。

3、向上調(diào)整過程分析(過程步驟如下圖):

a. 我們發(fā)現(xiàn)出入新元素10之后,10作為28(父節(jié)點)的子節(jié)點卻比28小,這樣就破壞了我們的堆結(jié)構(gòu),這樣就不構(gòu)成小堆。因此我們需要對該結(jié)構(gòu)進(jìn)行調(diào)整。

b.由于堆的物理結(jié)構(gòu)是一個數(shù)組,所以我們可以通過數(shù)組下標(biāo)的形式找到我們孩子節(jié)點的父節(jié)點。不難分析出parent = (child-1)/2.當(dāng)我們找到父節(jié)點時,我們進(jìn)行大小比較,如果子節(jié)點小于父節(jié)點,此時就要進(jìn)行交換元素。再讓子節(jié)點到父節(jié)點的位置,重新計算父節(jié)點。

c.持續(xù)循環(huán)比較,如果child等于0時,說明向上調(diào)整結(jié)束。因此循環(huán)的條件可寫為child>0.

注:循環(huán)過程中一旦成堆,則跳出循環(huán)。

功能實現(xiàn):

//交換函數(shù)
void Swap(HPDataType* pa, HPDataType* pb)
{
	HPDataType tmp = *pa;
	*pa = *pb;
	*pb = tmp;
}
 
 
//向上調(diào)整
void AdjustUp(HPDataType* a, size_t child)
{
	size_t parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}
 
 
void HeapPush(HP* php, HPDataType x)
{
	assert(php);
	//考慮是否擴(kuò)容
	if (php->size == php->capacity)
	{
		size_t newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* tmp = realloc(php->a, sizeof(HPDataType) * newCapacity);
		if (tmp == NULL)
		{
			printf("realloc failed\n");
			exit(-1);
		}
		php->a = tmp;
		php->capacity = newCapacity;
	}
	php->a[php->size] = x;
	++php->size;
	//需要向上調(diào)整
	AdjustUp(php->a, php->size - 1);
}

3.5 堆的刪除元素 HeapPop  *

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

功能分析:

我們要刪除堆是刪除堆頂?shù)臄?shù)據(jù),我們不能直接刪除堆頂?shù)臄?shù)據(jù)。如果直接刪除堆頂?shù)臄?shù)據(jù),就會破壞堆結(jié)構(gòu),并且復(fù)原的復(fù)雜度較高。在這里我們介紹一種方法不僅解決了刪除堆的問題,并且復(fù)雜度較低。

1、首先我們要將堆頂?shù)臄?shù)據(jù)跟最后一個數(shù)據(jù)交換,然后刪除數(shù)組最后一個數(shù)據(jù),再進(jìn)行向下調(diào)整算法。

2、向下調(diào)整算法具體步驟(過程步驟如下圖):

a.我們將堆頂元素和數(shù)組最后一個元素交換后,此時堆頂?shù)脑厥菙?shù)組的最后一個元素,我們要進(jìn)行向下調(diào)整。定義parent為堆頂元素,查找2個子節(jié)點中較小的一個節(jié)點作為孩子節(jié)點。由于堆是數(shù)組結(jié)構(gòu)實現(xiàn),我們可以首先找到左孩子節(jié)點child = 2*parent+1。讓左孩子和右孩子進(jìn)行比較,較小的作為child的最后值。

b.如果孩子小于父親,則交換,并繼續(xù)往下調(diào)整。讓parent到child的位置,再重新計算孩子。

c.當(dāng)孩子大于等于元素總個數(shù)時,循環(huán)結(jié)束。因此循環(huán)的條件可以寫為child<size.

注:循環(huán)過程中一旦成堆,則跳出循環(huán)。

功能實現(xiàn):

void AdjustDown(HPDataType* a, size_t size, size_t root)
{
	size_t parent = root;
	size_t child = parent * 2 + 1;//先拿到左孩子
	while (child < size)
	{
		// 1、選出左右孩子中小的那個
		if (child + 1 < size && a[child + 1] < a[child])
		{
			++child;
		}
 
		// 2、如果孩子小于父親,則交換,并繼續(xù)往下調(diào)整
		if (a[child] < a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}
void HeapPop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	Swap(&php->a[0], &php->a[php->size - 1]);
	--php->size;
	AdjustDown(php->a, php->size, 0);
}

3.6 判斷是否為空 HeapEmpty

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

3.7 求元素個數(shù)

size_t HeapSize(HP* php)
{
	assert(php);
	return php->size;
}

3.8 求堆頂元素

HPDataType HeapTop(HP* php)
{
	assert(php);
	assert(php->size > 0);
	return php->a[0];
}

 4.堆的應(yīng)用:堆排序   ***

堆排序即利用堆的思想來進(jìn)行排序,總共分為兩個步驟: 1. 建堆 升序:建大堆 降序:建小堆 2. 利用堆刪除思想來進(jìn)行排序 建堆和堆刪除中都用到了向下調(diào)整,因此掌握了向下調(diào)整,就可以完成堆排序。

假設(shè)此時我們需要對數(shù)組元素進(jìn)行升序排序,我們就可以使用我們剛剛實現(xiàn)的小堆。

4.1 堆排序?qū)崿F(xiàn)過程分析

1、首先我們將數(shù)組的元素插入到堆中,根據(jù)向上調(diào)整,此時堆已經(jīng)是小堆。

2、根據(jù)小堆的性質(zhì),堆頂?shù)脑匾欢ㄊ窃摱阎凶钚〉脑?,因此我們?nèi)〉蕉秧數(shù)脑兀賱h除堆頂?shù)脑刈尪阎匦律尚《?。依次循環(huán)即可解決升序排序(降序排序只需將小堆改為大堆即可)。

4.2 堆排序?qū)崿F(xiàn)代碼

//堆排序
void HeapSort(int* a, int size)
{
	HP hp;
	HeapInit(&hp);
	for (int i = 0; i < size; ++i)
	{
		HeapPush(&hp, a[i]);
	}
	size_t j = 0;
	while (!HeapEmpty(&hp))
	{
		a[j] = HeapTop(&hp);
		j++;
		HeapPop(&hp);
	}
	HeapDestory(&hp);
}
int main()
{
	//	TestHeap();
 
	int a[] = { 4,2,1,3,5,7,9,8,6};
	HeapSort(a,sizeof(a)/sizeof(int));
	for (int i = 0; i < sizeof(a) / sizeof(int); ++i)
	{
		printf("%d ", a[i]);
	}
	
	return 0;
}

4.3 堆排序結(jié)果演示

5.堆(小堆)的完整代碼

2022_03_30 -- 堆/2022_03_30 -- 二叉樹 · 李興宇/數(shù)據(jù)結(jié)構(gòu)

總結(jié)

到此這篇關(guān)于C語言數(shù)據(jù)結(jié)構(gòu)之堆、堆排序的分析及實現(xiàn)的文章就介紹到這了,更多相關(guān)C語言堆、堆排序?qū)崿F(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語言進(jìn)制轉(zhuǎn)換代碼分享

    C語言進(jìn)制轉(zhuǎn)換代碼分享

    本文給大家分享的是使用C語言實現(xiàn)進(jìn)制轉(zhuǎn)換的代碼,十分的簡單實用,有需要的小伙伴可以參考下。
    2015-07-07
  • C++獲取項目路徑的兩種方式詳解

    C++獲取項目路徑的兩種方式詳解

    這篇文章主要介紹了C++獲取項目路徑的兩種方式的相關(guān)資料,需要的朋友可以參考下,希望能夠給你帶來幫助
    2021-10-10
  • 利用C語言實現(xiàn)三子棋(井字棋)小游戲

    利用C語言實現(xiàn)三子棋(井字棋)小游戲

    這篇文章主要為大家詳細(xì)介紹了利用C語言實現(xiàn)三子棋小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Dev C++編譯時運(yùn)行報錯source file not compile問題

    Dev C++編譯時運(yùn)行報錯source file not compile問題

    這篇文章主要介紹了Dev C++編譯時運(yùn)行報錯source file not compile問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • C語言數(shù)據(jù)結(jié)構(gòu)之vector底層實現(xiàn)機(jī)制解析

    C語言數(shù)據(jù)結(jié)構(gòu)之vector底層實現(xiàn)機(jī)制解析

    向量(Vector)是一個封裝了動態(tài)大小數(shù)組的順序容器(Sequence?Container)。跟任意其它類型容器一樣,它能夠存放各種類型的對象??梢院唵蔚恼J(rèn)為,向量是一個能夠存放任意類型的動態(tài)數(shù)組
    2021-11-11
  • C/C++實現(xiàn)手寫數(shù)字識別的示例詳解

    C/C++實現(xiàn)手寫數(shù)字識別的示例詳解

    這篇文章主要為大家詳細(xì)介紹了如何使用C/C++實現(xiàn)手寫數(shù)字識別,分別處理 32*32 文本數(shù)據(jù)集和mnist 28*28 png數(shù)據(jù)集,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-10-10
  • C++中多線程間共享數(shù)據(jù)詳解

    C++中多線程間共享數(shù)據(jù)詳解

    這篇文章主要為大家詳細(xì)介紹了C++中多線程間共享數(shù)據(jù)的相關(guān)知識,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-01-01
  • C++使用MySQL-Connector/C++連接MySQL出現(xiàn)LNK2019錯誤的解決方法

    C++使用MySQL-Connector/C++連接MySQL出現(xiàn)LNK2019錯誤的解決方法

    這篇文章主要介紹了C++使用MySQL-Connector/C++連接MySQL出現(xiàn)LNK2019錯誤的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • 詳解為什么指針被譽(yù)為C語言靈魂

    詳解為什么指針被譽(yù)為C語言靈魂

    說到指針,就不可能脫離開內(nèi)存,學(xué)會指針的人分為兩種,一種是不了解內(nèi)存模型,另外一種則是了解。不了解的對指針的理解就停留在“指針就是變量的地址”這句話,會比較害怕使用指針,特別是各種高級操作。本文將帶你詳細(xì)了解C語言指針
    2021-06-06
  • c/c++中變量的聲明和定義深入解析

    c/c++中變量的聲明和定義深入解析

    “聲明”為編譯服務(wù),用于類型檢查 ;“定義”在運(yùn)行時會分配空間,不能重復(fù)定義,同時具備聲明的功能
    2013-09-09

最新評論