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

什么是堆,如何實現(xiàn)?(附堆排序,TOP-K問題)

這篇具有很好參考價值的文章主要介紹了什么是堆,如何實現(xiàn)?(附堆排序,TOP-K問題)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

歡迎來到 Claffic 的博客???????

什么是堆,如何實現(xiàn)?(附堆排序,TOP-K問題)

“春風(fēng)里,是誰 花一樣爛漫?”

前言:

二叉樹給大家講解的差不多了,接下來就是二叉樹的實際應(yīng)用了:這期我們來講堆,它是一種順序結(jié)構(gòu)的特殊二叉樹,可以實現(xiàn)排序等功能,那就讓我們開始吧!


目錄

??Part1: 何為堆

1.堆的概念

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

??Part2: 堆的實現(xiàn)?

1.前期準(zhǔn)備

1.1項目創(chuàng)建

1.2結(jié)構(gòu)定義

1.3堆的初始化

2.相關(guān)功能實現(xiàn)

2.1堆插入數(shù)據(jù)

2.2堆刪除數(shù)據(jù)?

2.3數(shù)組建堆

2.4判斷堆是否為空

2.5獲取堆頂元素

2.6堆的銷毀

??Part3: 堆的應(yīng)用

3.1堆排序(排升序)

3.2 TOP-K問題


Part1: 何為堆

1.堆的概念

將元素按照完全二叉樹的順序存儲方式存儲在一個一維數(shù)組中,并滿足對應(yīng)根結(jié)點數(shù)值大于兩個孩子結(jié)點(稱為大堆)或者小于兩個孩子結(jié)點(稱為小堆)。

單看概念是不足以理解的,與結(jié)構(gòu)結(jié)合理解會更好:

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

還記得上期的 邏輯結(jié)構(gòu) 物理結(jié)構(gòu) 嗎?

什么是堆,如何實現(xiàn)?(附堆排序,TOP-K問題)

堆的邏輯結(jié)構(gòu)是一顆 完全二叉樹?

堆的本質(zhì)呢,是 數(shù)組 ,不過對數(shù)組中存儲的數(shù)據(jù)順序有著特殊的要求:?

? 大堆:根節(jié)點數(shù)值大于兩個孩子結(jié)點的數(shù)值;

? 小堆:根節(jié)點數(shù)值小于兩個孩子結(jié)點的數(shù)值。

上例子:?

什么是堆,如何實現(xiàn)?(附堆排序,TOP-K問題)

這是一個大堆

什么是堆,如何實現(xiàn)?(附堆排序,TOP-K問題)

這是一個小堆

到這里,相信你已經(jīng)知道什么是堆了。

記住堆的性質(zhì):

? 堆總是一顆完全二叉樹

? 堆中某個結(jié)點的數(shù)值總是不大于不小于雙親結(jié)點

Part2: 堆的實現(xiàn)?

1.前期準(zhǔn)備

1.1項目創(chuàng)建

Heap.h:頭文件,聲明所有函數(shù);

Heap.c:源文件,實現(xiàn)各函數(shù);

Test.c:??源文件,主函數(shù)所在項,可調(diào)用各函數(shù)。

1.2結(jié)構(gòu)定義

堆的本質(zhì)就是一個數(shù)組嘛,

默認(rèn)整型數(shù)組,為方便相關(guān)操作的實現(xiàn),再定義大小size和容量capacity

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

1.3堆的初始化

這里選擇動態(tài)開辟內(nèi)存,

初始容量為4個整型大?。?

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

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

2.相關(guān)功能實現(xiàn)

注意:我們這里默認(rèn)實現(xiàn)大堆?

2.1堆插入數(shù)據(jù)

插入數(shù)據(jù)前,要先判斷下有沒有滿,定義容量和大小的好處就在這里,

直接通過 容量和大小是否相等 就可以,

如果滿,默認(rèn)擴展至先前容量的兩倍;

if (php->size == php->capacity)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * php->capacity*2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity *= 2;
	}
	php->a[php->size] = x;
	php->size++;

接下來就是對插入的數(shù)據(jù)進行調(diào)整了,

要注意:插入之前就是大堆或小堆(默認(rèn)大堆),

我們需要做的就是將插在末尾的數(shù)據(jù)通過比較交換,使其在最合適的位置

這里用到的是 向上調(diào)整

什么是堆,如何實現(xiàn)?(附堆排序,TOP-K問題)

第一次做這種動圖??????

如圖所示,在數(shù)組末尾插入數(shù)據(jù)后,需要跟雙親結(jié)點比較,再決定是否向上調(diào)整。

//向上調(diào)整
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	while (child > 0)
	{
		if (a[parent] < a[child])
		{
			Swap(&a[parent], &a[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
			break;
	}
}

?所以最終代碼:

//堆插入數(shù)據(jù) 向上調(diào)整 插入之前就是堆
void HeapPush(HP* php, HPDataType x)
{
	assert(php);

	if (php->size == php->capacity)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * php->capacity*2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity *= 2;
	}
	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

2.2堆刪除數(shù)據(jù)?

大堆刪除數(shù)據(jù),是指刪除 最大根節(jié)點 。

那么怎樣才能保證刪除第一個數(shù)據(jù)之后,剩余的結(jié)點的雙親結(jié)點與孩子結(jié)點關(guān)系不亂呢??

這里有一個巧妙的辦法:?

先將第一個結(jié)點與最后一個結(jié)點 交換 ,再 刪除 最后一個結(jié)點,最后 向下調(diào)整

什么是堆,如何實現(xiàn)?(附堆排序,TOP-K問題)

最后仍然保持是一個大堆

代碼實現(xiàn):

//堆刪除數(shù)據(jù)  向下調(diào)整
void HeapPop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a, php->size, 0);

}
//向下調(diào)整
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
        //先判斷child,防止越界,挑出較大的孩子結(jié)點
		if (child < n + 1 && a[child + 1] < a[child])
		{
			child++;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

2.3數(shù)組建堆

任意給一個數(shù)組,可不能保證它就是堆,

所以要利用數(shù)組來建堆,也叫用數(shù)組初始化堆。

前半部分的空間開辟不必多講,重點放在建堆(默認(rèn)建大堆)上:

方法一:向上調(diào)整

給一個數(shù)組,先從第一個數(shù)開始,逐漸擴大區(qū)間,每擴大一次就進行一次向上調(diào)整;

其實就是 模擬插入數(shù)值 罷了。


//建堆(默認(rèn)建大堆),向上調(diào)整
for (int i = 1; i < n; i++)
{
	AdjustUp(a, i);
}

方法二:向下調(diào)整

這種方法是從倒數(shù)第二層數(shù)組開始調(diào)整,依次向下調(diào)整。


//建堆,向下調(diào)整
for (int i = (n - 2) / 2; i >= 0; i--)
{
	AdjustDown(a, n, i);
}

兩種方法對比:?

這里先告訴大家結(jié)論:

向下調(diào)整建堆(時間復(fù)雜度為:O(N))要優(yōu)于向上調(diào)整建堆(時間復(fù)雜度為:O(N*logN)

準(zhǔn)確計算方式:其實就是每層的結(jié)點個數(shù)乘以要向下/向上調(diào)整的層數(shù),最后求和,比較下即可。

簡單理解:

向下調(diào)整:結(jié)點多的調(diào)整次數(shù)少,結(jié)點少的調(diào)整次數(shù)多;

向上調(diào)整:結(jié)點少的調(diào)整次數(shù)少,結(jié)點多的調(diào)整次數(shù)多;

就這個描述,向下調(diào)整更平衡一些,在這種粗略理解下,向下調(diào)整優(yōu)于向上調(diào)整。

2.4判斷堆是否為空

直接判斷大小是否為0即可。

//判空
bool HeapEmpty(HP* php)
{
	assert(php);

	return php->size == 0;
}

2.5獲取堆頂元素

返回數(shù)組的第一個值即可。

//獲取堆頂元素
HPDataType HeapTop(HP* php)
{
	assert(php);

	return php->a[0];
}

2.6堆的銷毀

將數(shù)組釋放置空,容量和大小歸零。?

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

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

Part3: 堆的應(yīng)用

3.1堆排序(排升序)

給你一個數(shù)組,要進行堆排序,首先要把它 變成堆

考慮下這個問題:排升序,建大堆還是小堆?

答案是 建大堆 ,

因為大堆的最大根節(jié)點就是整個堆中最大的,我們只需要將這個最大值調(diào)整到最后就可以了;

如果是小堆,我們不能保證兩個孩子結(jié)點的大小關(guān)系,調(diào)整起來就比大堆麻煩。

再者就是優(yōu)先選擇 向下調(diào)整 ,效率較高。

//向下調(diào)整(效率更高)
for (int i = (n - 1 - 1) / 2; i >= 0; i--)
{
	AdjustDown(a, n, i);
}

建完大堆,接下來就是數(shù)據(jù)位置的調(diào)整了:?

將最大根節(jié)點調(diào)整到最后,然后通過調(diào)整,保持剩余結(jié)點依然是一個大堆。

是不是感覺與刪除操作有些相似?

是的,這可以理解為 模擬刪除操作 ,只不過被刪除的數(shù)還是要保留的。

最終代碼:

//堆排序
void HeapSort(int* a, int n)
{
	//向下調(diào)整(效率更高)
	for (int i = (n - 1 - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, n, i);
	}
	int end = n - 1;
	while (end--)
	{
		Swap(&a[0], &a[end]);
		AdjustDown(a, end, 0);
	}
}

3.2 TOP-K問題

給一個情景:

在世界所有的企業(yè)中選出世界500強。

這就涉及到TOP-K問題,簡單理解,TOP-K問題就是在N個數(shù)字中選出K個數(shù)字,保證這K個數(shù)字任何一個都比剩余N-K個數(shù)字大。

建堆是必須的,但建大堆還是小堆?

這里有一個巧妙的辦法:

1.前K個數(shù)字先建成一個小堆;

2.遍歷剩余N-K個數(shù)字,遇到比堆頂數(shù)字大的就替它進堆,再進行向下調(diào)整。

這種方法巧妙在 大數(shù)向下沉,小數(shù)向上浮。最后大數(shù)把小數(shù)擠出堆,堆里的都是大數(shù)。

為了創(chuàng)建大量數(shù)據(jù),這里利用隨機數(shù),將數(shù)據(jù)存儲在文件當(dāng)中:

void PrintTopK(const char* file, int k)
{
	// 用a中前k個元素建小堆
	int* topk = (int*)malloc(sizeof(int) * k);
	assert(topk);

	FILE* fout = fopen(file, "r");
	if (fout == NULL)
	{
		perror("fopen error");
		return;
	}

	for (int i = 0; i < k; ++i)
	{
		fscanf(fout, "%d", &topk[i]);
	}

	for (int i = (k - 2) / 2; i >= 0; --i)
	{
		AdjustDown(topk, k, i);
	}

	// 將剩余n-k個元素依次與堆頂元素交換,不滿就替換
	int val = 0;
	int ret = fscanf(fout, "%d", &val);
	while (ret != EOF)
	{
		if (val > topk[0])
		{
			topk[0] = val;
			AdjustDown(topk, k, 0);
		}

		ret = fscanf(fout, "%d", &val);
	}

	for (int i = 0; i < k; i++)
	{
		printf("%d ", topk[i]);
	}
	printf("\n");

	free(topk);
	fclose(fout);
}

?

代碼已上傳至?我的gitee

拿走不謝~?


總結(jié):

這期知識密度較大,不僅有堆的實現(xiàn),還有兩個堆的應(yīng)用,建議先將堆實現(xiàn)一波,再進行兩個應(yīng)用問題的解決~~~

碼文不易?

如果你覺得這篇文章還不錯并且對你有幫助,不妨支持一波哦????????文章來源地址http://www.zghlxwxcb.cn/news/detail-409957.html

到了這里,關(guān)于什么是堆,如何實現(xiàn)?(附堆排序,TOP-K問題)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

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

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

相關(guān)文章

  • 堆的實現(xiàn),畫圖和代碼分析建堆,堆排序,時間復(fù)雜度以及TOP-K問題

    堆的實現(xiàn),畫圖和代碼分析建堆,堆排序,時間復(fù)雜度以及TOP-K問題

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

    2024年02月04日
    瀏覽(22)
  • 堆排序之“TOP-K”問題

    堆排序之“TOP-K”問題

    目錄 一、什么是TOP-K問題 二、解決思路? 一般的正常思路: 最優(yōu)的解決思路: 三、文件流中實踐TOP-K方法? 創(chuàng)建包含足夠多整數(shù)的文件: 向下調(diào)整算法 找出最大的K個數(shù) 完整版代碼: 前面我已經(jīng)學(xué)習(xí)過使用“堆排序”對數(shù)組排降序了,接下來再來看一個堆排序的應(yīng)用場景。

    2024年02月06日
    瀏覽(18)
  • 數(shù)據(jù)結(jié)構(gòu)與算法:堆排序和TOP-K問題

    數(shù)據(jù)結(jié)構(gòu)與算法:堆排序和TOP-K問題

    朋友們大家好,本節(jié)內(nèi)容來到堆的應(yīng)用:堆排序和topk問題 我們在c語言中已經(jīng)見到過幾種排序,冒泡排序,快速排序(qsort) 冒泡排序的時間復(fù)雜度為O(N 2 ),空間復(fù)雜度為O(1);qsort排序的時間復(fù)雜度為 O(nlogn),空間復(fù)雜度為O(logn),而今天所講到的堆排序在時間與空間復(fù)雜度上相

    2024年03月08日
    瀏覽(35)
  • 堆(兩種建堆方法)、堆排序和Top-K問題

    堆(兩種建堆方法)、堆排序和Top-K問題

    是一種完全二叉樹,分為大堆,小堆 如果有一個關(guān)鍵碼的集合 int K[ ] = {27,15,19,18,28,34,65,49,25,37} ; 把它的所有元素按完全二叉樹的順序存儲方式存儲 在一個一維數(shù)組中,并滿足:K i =K 2*i+1 且K i =K 2*i+2 ( K i =K 2*i+1 且K i =K 2*i+2 ) i = 0,1, 2…,則稱為小堆(或大堆)。將根節(jié)點最大

    2023年04月09日
    瀏覽(22)
  • 數(shù)據(jù)結(jié)構(gòu)之堆排序以及Top-k問題詳細解析

    數(shù)據(jù)結(jié)構(gòu)之堆排序以及Top-k問題詳細解析

    個人主頁:點我進入主頁 專欄分類:C語言初階? ? ??C語言程序設(shè)計————KTV? ? ? ?C語言小游戲? ? ?C語言進階 C語言刷題? ? ? ?數(shù)據(jù)結(jié)構(gòu)初階 歡迎大家點贊,評論,收藏。 一起努力 目錄 1.前言 2.堆排序 2.1降序排序 2.2時間復(fù)雜度 3.Top-k問題 4.總結(jié) ? ? ? ? 在上一篇文

    2024年02月05日
    瀏覽(29)
  • 【數(shù)據(jù)結(jié)構(gòu)】---堆排序+TOP-K問題(了解游戲排行底層原理)

    【數(shù)據(jù)結(jié)構(gòu)】---堆排序+TOP-K問題(了解游戲排行底層原理)

    ??個人主頁:@小沈熬夜禿頭中???? ??小編介紹:歡迎來到我的亂七八糟小星球?? ??專欄:數(shù)據(jù)結(jié)構(gòu) ??本章內(nèi)容:堆排序+TOP-K問題 送給各位??:日落跌入昭昭星野 人間忽晚 山河以秋 記得 評論?? +點贊?? +收藏?? +關(guān)注??哦~ 提示:以下是本篇文章正文內(nèi)容,下面

    2024年02月06日
    瀏覽(23)
  • 【數(shù)據(jù)結(jié)構(gòu)】二叉樹-堆(top-k問題,堆排序,時間復(fù)雜度)

    【數(shù)據(jù)結(jié)構(gòu)】二叉樹-堆(top-k問題,堆排序,時間復(fù)雜度)

    ???個人主頁: 秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343 ???系列專欄: 《數(shù)據(jù)結(jié)構(gòu)》https://blog.csdn.net/qinjh_/category_12536791.html?spm=1001.2014.3001.5482 ?? 目錄 堆排序 第一種 ??編輯 第二種 ?TOP-K問題 建堆的時間復(fù)雜度 向下調(diào)整建堆的時間復(fù)雜度: ?向上調(diào)整建堆的時間

    2024年01月21日
    瀏覽(32)
  • 數(shù)據(jù)結(jié)構(gòu)進階篇 之 【堆的應(yīng)用】(堆排序,TOP-K問題)詳細講解

    數(shù)據(jù)結(jié)構(gòu)進階篇 之 【堆的應(yīng)用】(堆排序,TOP-K問題)詳細講解

    所有人都關(guān)心我飛的高不高,只有我媽關(guān)心我翅膀硬不硬 1.1 建堆 1.2 利用堆刪除思想來進行排序 –?–?–?–?–?–?–?–?–?–?–?–?–?–?–?-正文開始-?–?–?–?–?–?–?–?–?–?–?–?–?–?–?– 學(xué)習(xí)一個知識,我們起碼要直

    2024年04月10日
    瀏覽(19)
  • 數(shù)據(jù)結(jié)構(gòu)(C語言實現(xiàn))——二叉樹的概念及二叉樹順序結(jié)構(gòu)和鏈?zhǔn)浇Y(jié)構(gòu)的實現(xiàn)(堆排序+TOP-K問題+鏈?zhǔn)蕉鏄湎嚓P(guān)操作)

    數(shù)據(jù)結(jié)構(gòu)(C語言實現(xiàn))——二叉樹的概念及二叉樹順序結(jié)構(gòu)和鏈?zhǔn)浇Y(jié)構(gòu)的實現(xiàn)(堆排序+TOP-K問題+鏈?zhǔn)蕉鏄湎嚓P(guān)操作)

    前面學(xué)習(xí)了數(shù)據(jù)結(jié)構(gòu)中線性結(jié)構(gòu)的幾種結(jié)構(gòu),順序表,鏈表,棧和隊列等,今天我們來學(xué)習(xí)一種非線性的數(shù)據(jù)結(jié)構(gòu)——樹。由于二叉樹是數(shù)據(jù)結(jié)構(gòu)中的一個重點和難點,所以本文著重介紹二叉樹的相關(guān)概念和性質(zhì),以及二叉樹的應(yīng)用。 樹是一種非線性的數(shù)據(jù)結(jié)構(gòu),它是由n(

    2023年04月21日
    瀏覽(23)
  • 數(shù)據(jù)結(jié)構(gòu)--堆的實現(xiàn)-大根堆/小根堆/堆排序/堆排序穩(wěn)定性證明/TOP-K

    數(shù)據(jù)結(jié)構(gòu)--堆的實現(xiàn)-大根堆/小根堆/堆排序/堆排序穩(wěn)定性證明/TOP-K

    ? ? ? ? 前言? ? ? ? ? 逆水行舟,不進則退?。。? ? ? ? ? ? ? ? 目錄 ? ?? ? 認(rèn)識堆 ? ?? ? 堆的創(chuàng)建 ? ? ? ? 1,向下調(diào)整的方法建立堆 ? ? ? ? 2,以向下調(diào)整的方式建立小根堆 ? ? ? ? 3,向上調(diào)整的方式建堆 ? ?? ? 堆的插入 ? ?? ? 堆的刪除? ? ? ? ? ?? ?

    2024年02月04日
    瀏覽(23)

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

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

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

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

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包