數據結構(C):從初識堆到堆排序的實現

目錄

🌞0.前言

🚈?1.堆的概念

🚈?2.堆的實現

🚝2.1堆向下調整算法

🚝2.2堆的創建(堆向下調整算法)

??2.2.1 向下調整建堆時間復雜度

🚝2.3堆向上調整算法

🚝2.4堆的創建(堆向上調整算法)

??2.4.1向上調整算法建堆的時間復雜度

🚝2.5堆的插入

🚝2.6堆的刪除

🚝2.7建堆的代碼實現

??2.7.1向下調整實現堆的建立

??2.7.2向上調整實現堆的建立

🚈?3.完整的堆的代碼的實現

🚝3.1堆的創建

🚝3.2堆的銷毀

🚝3.3堆的插入

🚝3.4堆的刪除(頭刪)

🚝3.5取堆頂元素的數據

🚝3.6 堆的數據個數

🚝3.7堆的判空

🚈?4.堆的應用堆排序

?5.結束語


🌞0.前言

? ? ? ? 言C之言,聊C之識,以C會友,共向遠方。各位博友的各位你們好啊,這里是持續分享數據結構知識的小趙同學,今天要分享的數據結構知識是,在這一章,小趙將會向大家展開聊聊的相關知識。?

🚈?1.堆的概念

堆就是以 二叉樹的順序存儲方式來存儲元素,同時又要滿足 父親結點存儲數據都要大于等于兒子結點存儲數據(也可以是父親結點數據都要小于等于兒子結點數據)的一種數據結構。堆只有兩種即大堆和小堆,大堆就是父親結點數據大于等于兒子結點數據,小堆則反之。

同時這里要注意的是堆一定是完全二叉樹,不然就不是堆。那完全二叉樹是什么呢?這個地方不懂的博友可以看我們的這一篇博客:數據結構(C)樹的概念和二叉樹初見http://t.csdnimg.cn/JnWfb

如果看完了還是不明白可以私信小趙詢問哦。

好了下面讓我們看看上面說的兩種堆在圖上是怎么呈現的呢?

?

我們發現我們的任意一個父節點都比他的兩個子節點大(或等于)這個時候這就是一個大堆。?

我們發現我們的任意一個父節點都比他的兩個子節點小(或等于)這個時候這就是一個小堆。?

雖然堆是完全二叉樹,但其的存儲方式卻與二叉樹不同,我們一般存儲堆的方式是數組。而我們上面所畫的叫邏輯結構,那怎么由數組的存儲方式,轉化為我們的邏輯結構呢?

我們在介紹二叉樹的時候其實也曾簡單的說過二叉樹是有順序的,是從上到下,從左向右的,按這個順序我們就可以給我們的二叉樹標序號。?

那么就可以我們的邏輯結構轉化成我們的數組結構了,既然我們已經會將邏輯結構轉化為數組結構了,那么將數組結構轉化為邏輯結構也就沒有這么難了,大家可以自己試試,如果實在實現不了也可以找小趙咨詢哦。?

🚈?2.堆的實現

🚝2.1堆向下調整算法

什么是向下調整算法呢?其實正如其名就是從上到下調整堆的意思。要想更深入了解這個東西就先來看這張圖。

看這張圖這是一個很明顯的小堆對吧,那我這個時候要你改成一個大堆怎么辦,這個時候的操作其實是2,3進行比較,然后拿出大的和1換這個就成大堆了。?

可如果這個時候我給你的是個這樣的堆你又該怎么辦(要改成小堆)

?其實這個時候也是可以操作的,因為下面是有序的堆,我們只需要按照前面的順序一步步來就可以完成了。只不過這個時候改成了選擇兩個子中的小的哪一個,因為我們要做得是小堆(這個時候之所以說他下面是有序的堆是因為蓋住最上面的一個,下面的兩個堆都是小堆,我們要改成的也是小堆。)

那如果是無序的呢??(改成小堆)

這個時候我們發現我們再想把這個改成小堆的難度就很大了。

🚝2.2堆的創建(堆向下調整算法)

那通過上面的實驗我們發現,想通過一個位置來做上面的操作,并把整個堆都變成小(大)堆,必須下面就是一個小(大)堆。所以我們的向下調整其實也是從最下面的開始的,而且我們剛剛做的步驟其實就是向下調整。?

那么再面對上面那個無序的堆我們也就有方法了。

這個時候我們就可以做到將無序的堆轉化成有序的堆了。?那么這個時候我們面對任何一個無序的數組,都可以通過這樣的方式將他轉化成堆.(至少在邏輯圖上可以實現,代碼實現下面說)

??2.2.1 向下調整建堆時間復雜度

每層節點個數 × 最壞情況向下調整次數:
T(N) = 2^(h-2) × 1 + 2^(h-3) × 2 + … … + 2^1 × (h-2)+2^0*(h-1)

錯位相減法
等號左右兩邊乘個 2 得到一個新公式,再用新公式減去舊的公式,具體見下圖

🚝2.3堆向上調整算法

聊了向下調整算法,那么其實也有向上調整算法。

如這里我們如何把這個堆改成大堆

向上調整一般是怎么玩的呢,一般我們的向上調整法就是從下面往上調整,向這里要想調整到我們想要的樣子就要三步

🚝2.4堆的創建(堆向上調整算法)

這里呢也和上面一樣,向上調整的地方的上面必須是已經調整好的,所以我們一般用向上調整法去建堆的時候往往要從第一個開始建堆到最下面。

??2.4.1向上調整算法建堆的時間復雜度

T(N) = 2^1× 1 + 2^2× 2 + 2^3 ×3+ … … + 2^(h-3)× (h-3) + 2^(h-2) × (h-2)+ 2^(h-1) × (h-1)

綜上:向下調整建堆要更優秀,效率更高

但總的來建堆的時間復雜度是O(N*logN)

🚝2.5堆的插入

像我們一般堆是用數組存儲的,我們一般插入也是從數組后面插入,所以也就是在最后一個位置插入,而這個時候上面的堆都是有順序的,我們可以用我們的向上調整算法來解決堆的插入。

🚝2.6堆的刪除

堆的刪除,我們一般針對的是頭元素的刪除,這個時候我們采取的策略是將頭元素和尾元素交換,然后讓頭元素,向下調整(因為下面的堆都已經是有順序的)

這樣就可以完成頭刪了?

🚝2.7建堆的代碼實現

代碼的實現我們的表示表示方式和前面的雙親節點是對應的(parent),然后子節點就是(child)

??2.7.1向下調整實現堆的建立

//小堆的建立
void Swap(int* a, int* b)//交換
{int tmp = *a;*a = *b;*b = tmp;
}
//小堆
void ADjustDown(int *a,int parent,int n)
{int child = parent * 2 + 1;//子節點和雙親節點的關系while (child < n){if (child + 1 < n && a[child + 1] < a[child])//選出兩個孩子中較小的一個{child++;}if (a[child] < a[parent])//若子節點比雙親節點小{Swap(&a[child], &a[parent]);//交換接著向下進行parent = child;child = parent * 2 + 1;}else{break;}}
}
void Heap(int *a,int n)
{int i = (n - 1 - 1) / 2;//找到最后一個雙親節點while (i >= 0){ADjustDown(a, i, n);i--;}
}
int main()
{int a[10] = { 3,5,9,8,6,1,5,2,6,323 };Heap(a, 10);
}

這里的主要方式就是我們前面的圖所演示的,就是從最后一個雙親節點向下調整,然后調整過程中要小心不要讓字節出了數組的范圍,同時要記住子節點和雙親節點的關系。?這樣可以方便我們的代碼的實現。

??2.7.2向上調整實現堆的建立

//建立小堆
void Swap(int* a, int* b)//交換
{int tmp = *a;*a = *b;*b = tmp;
}
void ADjustUp(int*a,int child,int n)
{int parent = (child - 1) / 2;//雙親節點和子節點關系while (parent >= 0){if (a[child] < a[parent])//如果子節點比雙親節點小,那么交換,并且向上移動{Swap(&a[child], &a[parent]);child = parent;parent = (child - 1 - 1) / 2;}else{break;}}
}
void Heap(int* a, int n)
{int i = 0;while (i < n)//從最上面開始建堆{ADjustUp(a, i, n);i++;}
}
int main()
{int a[10] = { 3,5,9,87,6,12,7,11,13,1 };Heap(a, 10);
}

這里也基本上就是我們上面邏輯的實現,向上建堆,就要保證上面都是已經建好的堆,那么就得從首元素開始依次向下,建堆。?

🚈?3.完整的堆的代碼的實現

要想實現完整的堆的實現就要實現以下幾個函數

typedef int HPDataType;
typedef struct Heap
{HPDataType* _a;int _size;int _capacity;
}Heap;
// 堆的構建
void HeapCreate(Heap* hp, HPDataType* a, int n);
// 堆的銷毀
void HeapDestory(Heap* hp);
// 堆的插入
void HeapPush(Heap* hp, HPDataType x);
// 堆的刪除
void HeapPop(Heap* hp);
// 取堆頂的數據
HPDataType HeapTop(Heap* hp);
// 堆的數據個數
int HeapSize(Heap* hp);
// 堆的判空
int HeapEmpty(Heap* hp);

這樣我們的堆就可以像之前的鏈表順序表等一樣使用了。?

🚝3.1堆的創建

和上面的代碼邏輯基本一樣。

這里我們主要采用的是向下排序建堆,因為向下排序的時間復雜度低。

void HeapCreate(Heap* hp, HPDataType* a, int n)
{hp->_a = (HPDataType*)malloc(sizeof(HPDataType) * n);if (hp->_a == NULL){perror("malloc failed");}memcpy(hp->_a, a, n);hp->_capacity = n;hp->_size = n;int i = (n - 1 - 1) / 2;while (i >= 0){ADjustDown(a, i, n);i--;}
}

🚝3.2堆的銷毀

void HeapDestory(Heap* hp)
{free(hp->_a);hp->_a = NULL;hp->_capacity = 0;hp->_size = 0;
}

🚝3.3堆的插入

void HeapPush(Heap* hp, HPDataType x)
{if (hp->_size == hp->_capacity){int newcapacity = (hp->_size == 0 ? 4 : hp->_capacity * 2);//如果沒有內存就給4,有就是原來內存的雙倍HPDataType* tmp = (HPDataType*)realloc(hp->_a, sizeof(HPDataType) * newcapacity);if (tmp == NULL){perror("realloc failed");exit(-1);//退出程序,返回-1;}hp->_a = tmp;hp->_capacity = newcapacity;}hp->_a[hp->_size] = x;hp->_size++;ADjustUp(hp->_a, hp->_size - 1, hp->_size);//上面都是建好的堆,只需要向上調整即可
}

🚝3.4堆的刪除(頭刪)

void HeapPop(Heap* hp)
{if (!HeapEmpty(hp)){Swap(&(hp->_a[0]), &(hp->_a[hp->_size - 1]));//首尾交換ADjustDown(hp->_a, 0, hp->_size);//向下調整hp->_size--;//刪除尾}else{return;}
}

🚝3.5取堆頂元素的數據

HPDataType HeapTop(Heap* hp)
{if (!HeapEmpty(hp))//不是空集,返回首元素(即堆頂元素){return hp->_a[0];}else{return NULL;}
}

🚝3.6 堆的數據個數

int HeapSize(Heap* hp)
{return hp->_size;
}

🚝3.7堆的判空

int HeapEmpty(Heap* hp)
{return hp->_size == 0;//如果等式成立返回非零數,不成立返回零
}

🚈?4.堆的應用堆排序

下面我們進入堆排序的學習,堆排序是我們的排序中比較優的一個排序,他具體是如何實現的呢?其實我們在前面的知識中已經發現了,就是如果我們建的是小堆,那小堆的堆頂元素一定是整個堆里面最小的,大堆的堆頂元素一定是最大的,那么如果我們想排一個順序的方法就出來了,先把數組弄成一個大堆,再把第一個元素和最后一個元素交換,然后這個時候我們就排好了最大的一個,接著我們不管最后一個元素,對前面的n-1個元素再次建成大堆(其實只要進行向下調整就可以了因為下面的已經是大堆了),再重復上述操作。

那么我們就可以試著用代碼實現了。

//建大堆
void ADjustDown(int* a, int parent, int n)
{int child = parent * 2 + 1;//子節點和雙親節點的關系while (child < n){if (child + 1 < n && a[child + 1] > a[child])//選出兩個孩子中較大的一個{child++;}if (a[child] > a[parent])//若子節點比雙親節點大{Swap(&a[child], &a[parent]);//交換接著向下進行parent = child;child = parent * 2 + 1;}else{break;}}
}
void Heapsort(int* a, int n)
{int i = (n - 1 - 1) / 2;while (i >= 0){ADjustDown(a, i, n);i--;}//建堆while (n > 0){n--;Swap(&a[0], &a[n]);//交換首尾元素位置ADjustDown(a, 0, n);//下面都是建好的,只要向下調整就可以}
}
int main()
{int a[10] = { 3,5,9,87,6,12,7,11,13,1 };Heapsort(a,10);
}

?5.結束語

好了小趙今天的分享就到這里了,如果大家有什么不明白的地方可以在小趙的下方留言哦,同時如果小趙的博客中有什么地方不對也希望得到大家的指點,謝謝各位家人們的支持。你們的支持是小趙創作的動力,加油。

如果覺得文章對你有幫助的話,還請點贊,關注,收藏支持小趙,如有不足還請指點,方便小趙及時改正,感謝大家支持!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/21012.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/21012.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/21012.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

testcontainer

在我們的項目中&#xff0c;單元測試是保證我們代碼質量非常重要的一環&#xff0c;但是我們的業務代碼不可避免的需要依賴外部的系統或服務如DB&#xff0c;redis&#xff0c;其他外部服務等。如何保證我們的測試代碼不受外部依賴的影響&#xff0c;能夠穩定的運行成為了一件比…

c++------類和對象(下)包含了this指針、構造函數、析構函數、拷貝構造等

文章目錄 前言一、this指針1.1、this指針的引出1.2、 this指針的特性 二、類的默認的六個構造函數2.1、構造函數簡述2.2構造函數 三、析構函數3.1、析構函數引出3.2、特點&#xff1a; 四、拷貝構造4.1、引入4.2、特征&#xff1a;4.3、默認拷貝構造函數 總結 前言 在本節中&a…

中國的歷史看中國的經濟發展

從中國的歷史看中國的經濟發展&#xff0c;可以發現其經歷了幾個顯著的階段&#xff0c;每個階段都有其獨特的特點和成就&#xff1a; 古代經濟&#xff1a;中國古代經濟以農業為主&#xff0c;實行井田制&#xff0c;重視水利工程的建設&#xff0c;如都江堰、靈渠等。 商業發…

Compose Multiplatform 1.6.10 發布,解釋一些小問題, Jake 大佬的 Hack

雖然一直比較關注跨平臺開發&#xff0c;但其實我很少寫 Compose Multiplatform 的內容&#xff0c;因為關于 Compose Multiplatform 的使用&#xff0c;其實我并沒在實際生產環境上發布過&#xff0c;但是這個版本確實值得一提&#xff0c;因為該版本包含&#xff1a; iOS Bet…

數據庫(15)——DQL分頁查詢

DQL分頁查詢語法 SELECT 字段列表 FROM 表名 LIMIT 起始索引&#xff0c;查詢記錄數; 注&#xff1a;起始索引從0開始&#xff0c;起始索引&#xff08;查詢頁碼-1&#xff09;*每頁顯示記錄數。 如果查詢的是第一頁&#xff0c;可以省略起始索引。 示例&#xff1a;查詢第一頁…

【考研數學】概率論如何復習?跟誰好?

概率論一定要跟對老師&#xff0c;如果跟對老師&#xff0c;考研基本上能拿滿分 概率論在考研試卷中占比并不大&#xff0c;其中&#xff1a; 高等數學&#xff0c;90分&#xff0c;約占比60%; 線性代數&#xff0c;30分&#xff0c;約占比20%; 概率論與數理統計&#xff0…

hive中的join操作及其數據傾斜

hive中的join操作及其數據傾斜 join操作是一個大數據領域一個常見的話題。歸根結底是由于在數據量超大的情況下&#xff0c;join操作會使內存占用飆升。運算的復雜度也隨之上升。在進行join操作時&#xff0c;也會更容易發生數據傾斜。這些都是需要考慮的問題。 過去了解到很…

每日5題Day15 - LeetCode 71 - 75

每一步向前都是向自己的夢想更近一步&#xff0c;堅持不懈&#xff0c;勇往直前&#xff01; 第一題&#xff1a;71. 簡化路徑 - 力扣&#xff08;LeetCode&#xff09; class Solution {public String simplifyPath(String path) {Deque<String> stack new LinkedList…

mysql的增刪查改(進階)

目錄 一. 更復雜的新增 二. 查詢 2.1 聚合查詢 COUNT SUM AVG MAX MIN 2.1.2 分組查詢 group by 子句 2.1.3 HAVING 2.2 聯合查詢/多表查詢 2.2.1 內連接 2.2.2 外連接 2.2.3 全外連接 2.2.4 自連接 2.2.5 子查詢 2.2.6 合并查詢 一. 更復雜的新增 將從表名查詢到…

自動化辦公01 smtplib 郵件?動發送

目錄 一、準備需要發送郵件的郵箱賬號 二、發送郵箱的基本步驟 1. 登錄郵箱 2. 準備數據 3. 發送郵件 三、特殊內容的發送 1. 發送附件 2. 發送圖片 3. 發送超文本內容 4.郵件模板內容 SMTP&#xff08;Simple Mail Transfer Protocol&#xff09;即簡單郵件傳輸協議…

霍夫曼樹教程(個人總結版)

背景 霍夫曼樹&#xff08;Huffman Tree&#xff09;是一種在1952年由戴維霍夫曼&#xff08;David A. Huffman&#xff09;提出的數據壓縮算法。其主要目的是為了一種高效的數據編碼方法&#xff0c;以便在最小化總編碼長度的情況下對數據進行編碼。霍夫曼樹通過利用出現頻率…

【Qt秘籍】[009]-自定義槽函數/信號

自定義槽函數 在Qt中自定義槽函數是一個直接的過程&#xff0c;槽函數本質上是類的一個成員函數&#xff0c;它可以響應信號。所謂的自定義槽函數&#xff0c;實際上操作過程和定義普通的成員函數相似。以下是如何在Qt中定義一個自定義槽函數的步驟&#xff1a; 步驟 1: 定義槽…

<jsp:setProperty>設置有參構造函數創建的自定義對象的屬性

假設某一個類&#xff08;如TextConverter類&#xff09;有一個無參構造函數和一個有參構造函數&#xff0c;我們可以在Servlet里面先用有參構造函數自己new一個對象出來&#xff0c;存到request.setAttribute里面去。 Servlet轉發到jsp頁面后&#xff0c;再在jsp頁面上用<j…

django基于大數據+Spring的新冠肺炎疫情實時監控系統設計和實現

設計一個基于Django(后端)和Spring(可能的中間件或服務集成)的新冠肺炎疫情實時監控系統涉及多個方面,包括數據收集、數據處理、數據存儲、前端展示以及可能的中間件服務(如Spring Boot服務)。以下是一個大致的設計和實現步驟: 1. 系統架構 前端:使用Web框架(如Reac…

三種字符串的管理方式

NSString的三種實現方式 OC這個語言在不停的升級自己的內存管理&#xff0c;盡量的讓自己的 OC的字符串 問題引入 在學習字符串的過程中間會遇到一個因為OC語言更新造成的問題 例如&#xff1a; int main(int argc, const char * argv[]) {autoreleasepool {NSString* str1 …

C++核心編程類的總結封裝案例

C類的總結封裝案例 文章目錄 C類的總結封裝案例1.立方體類的封裝2.點與圓的關系的封裝3.總結 1.立方體類的封裝 在C中&#xff0c;我們可以定義一個立方體&#xff08;Cube&#xff09;類來封裝立方體的屬性和方法。立方體的屬性可能包括邊長&#xff08;side length&#xff…

【redis】set和zset常用命令

set 無序集合類型 sadd 和 smembers SADD&#xff1a;將一個或者多個元素添加到set中。注意,重復的元素無法添加到set中。 語法&#xff1a;SADD key member [member] 把集合中的元素,叫做member,就像hash類型中,叫做field類似. 返回值表示本次操作,添加成功了幾個元素. 時間復…

網絡原理——http/https ---http(1)

T04BF &#x1f44b;專欄: 算法|JAVA|MySQL|C語言 &#x1faf5; 今天你敲代碼了嗎 網絡原理 HTTP/HTTPS HTTP,全稱為"超文本傳輸協議" HTTP 誕?與1991年. ?前已經發展為最主流使?的?種應?層協議. 實際上,HTTP最新已經發展到 3.0 但是當前行業中主要使用的HT…

概念解析 | 為什么SAR中的天線間隔需要是四分之一波長?

注1:本文系“概念解析”系列之一,致力于簡潔清晰地解釋、辨析復雜而專業的概念。本次辨析的概念是:為什么SAR中的天線間隔需要是四分之一波長 概念解析 | 為什么SAR中的天線間隔需要是四分之一波長? 在這篇文章中,我們將深入探討**合成孔徑雷達(SAR)**系統中,為什么天…

明日周刊-第12期

以前小時候最期待六一兒童節了&#xff0c;父母總會給你滿足一個愿望&#xff0c;也許是一件禮物也許是一次陪伴。然而這個世界上其實還有很多兒童過不上兒童節&#xff0c;比如某些地區的小孩子&#xff0c;他們更擔心的是能不能見到明天的太陽。 文章目錄 一周熱點航天探索火…