25.6.19學習總結

什么是堆(Heap)?

堆是一種特殊的樹形數據結構,它滿足以下兩個主要屬性:

  1. 結構性(完全二叉樹):

    • 堆總是一個完全二叉樹 (Complete Binary Tree)。這意味著,除了最后一層,所有的層都是完全填充的,并且最后一層的所有節點都盡可能地向左填充。

    • 這個特性使得堆非常適合用數組來表示,因為節點之間的父子關系可以通過索引輕松計算。

  2. 堆序性(Heap Property):

    • 最大堆(Max-Heap):?在一個最大堆中,每個父節點的值都大于或等于其子節點的值。這意味著,根節點是整個堆中的最大元素。

    • 最小堆(Min-Heap):?在一個最小堆中,每個父節點的值都小于或等于其子節點的值。這意味著,根節點是整個堆中的最小元素。

總結:?堆是一個用數組實現的完全二叉樹,并且滿足特定的堆序性(父節點大于等于子節點或小于等于子節點)。

堆的ADT(抽象數據類型)操作

一個典型的堆通常支持以下操作:

  1. createHeap(capacity): 創建一個指定容量的空堆。

  2. isFull(heap): 檢查堆是否已滿。

  3. isEmpty(heap): 檢查堆是否為空。

  4. insert(heap, value): 將新元素插入堆中,并保持堆的性質。

  5. deleteMax(heap)?/?deleteMin(heap): 刪除堆中最大/最小元素(通常是根節點),并保持堆的性質。

  6. peekMax(heap)?/?peekMin(heap): 查看堆中最大/最小元素(根節點),但不刪除。

  7. heapifyUp(heap, index): 向上調整堆,當子節點的值發生變化(通常是插入操作后)。

  8. heapifyDown(heap, index): 向下調整堆,當父節點的值發生變化(通常是刪除操作或建堆操作后)。

  9. buildHeap(array, size): 將一個無序數組轉換為一個堆。

堆的C語言實現原理(基于數組)

由于堆是完全二叉樹,它最常用的實現方式是使用數組。這種實現方式的優點在于:

  • 空間效率高:?無需存儲指針。

  • 計算效率高:?父子節點的關系可以通過簡單的算術運算得出。

對于一個基于0索引的數組?arr

  • 根節點:?arr[0]

  • 節點?i?的左子節點:?arr[2 * i + 1]

  • 節點?i?的右子節點:?arr[2 * i + 2]

  • 節點?i?的父節點:?arr[(i - 1) / 2]?(注意整數除法)

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h> // For bool type// -----------------------------------------------------------
// 1. 定義堆結構體
// -----------------------------------------------------------
typedef struct MaxHeap {int* arr;       // 存儲堆元素的數組int capacity;   // 堆的最大容量int size;       // 堆當前元素的數量
} MaxHeap;// -----------------------------------------------------------
// 2. 輔助函數
// -----------------------------------------------------------// 交換兩個整數的值
void swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp;
}// -----------------------------------------------------------
// 3. 核心堆操作函數
// -----------------------------------------------------------// 創建一個空的MaxHeap
MaxHeap* createMaxHeap(int capacity) {MaxHeap* newHeap = (MaxHeap*)malloc(sizeof(MaxHeap));if (newHeap == NULL) {perror("Failed to allocate memory for MaxHeap structure");exit(EXIT_FAILURE);}newHeap->arr = (int*)malloc(sizeof(int) * capacity);if (newHeap->arr == NULL) {perror("Failed to allocate memory for MaxHeap array");free(newHeap); // Clean up partially allocated memoryexit(EXIT_FAILURE);}newHeap->capacity = capacity;newHeap->size = 0;printf("MaxHeap created with capacity: %d\n", capacity);return newHeap;
}// 釋放堆占用的內存
void destroyMaxHeap(MaxHeap* heap) {if (heap != NULL) {free(heap->arr);heap->arr = NULL;free(heap);printf("MaxHeap destroyed.\n");}
}// 檢查堆是否為空
bool isEmpty(MaxHeap* heap) {return heap->size == 0;
}// 檢查堆是否已滿
bool isFull(MaxHeap* heap) {return heap->size == heap->capacity;
}// 獲取父節點的索引
int getParentIndex(int i) {return (i - 1) / 2;
}// 獲取左子節點的索引
int getLeftChildIndex(int i) {return 2 * i + 1;
}// 獲取右子節點的索引
int ietRightChildIndex(int i) {return 2 * i + 2;
}// 判斷給定索引是否具有左子節點
bool hasLeftChild(MaxHeap* heap, int i) {return getLeftChildIndex(i) < heap->size;
}// 判斷給定索引是否具有右子節點
bool hasRightChild(MaxHeap* heap, int i) {return ietRightChildIndex(i) < heap->size;
}/*** @brief 向上調整堆 (Heapify Up)* 當子節點的值大于父節點時,將其與父節點交換,直到滿足堆性質或到達根節點。* 通常在插入新元素后調用。** @param heap 指向MaxHeap的指針* @param index 待調整元素的當前索引*/
void heapifyUp(MaxHeap* heap, int index) {// 當當前節點不是根節點,且當前節點的值大于其父節點的值時while (index > 0 && heap->arr[getParentIndex(index)] < heap->arr[index]) {swap(&heap->arr[index], &heap->arr[getParentIndex(index)]);index = getParentIndex(index); // 移動到父節點的位置,繼續向上檢查}
}/*** @brief 向下調整堆 (Heapify Down)* 當父節點的值小于其子節點時,將其與最大的子節點交換,直到滿足堆性質或到達葉子節點。* 通常在刪除根元素或建堆時調用。** @param heap 指向MaxHeap的指針* @param index 待調整元素的當前索引*/
void heapifyDown(MaxHeap* heap, int index) {int maxIndex = index;int leftChild = getLeftChildIndex(index);int rightChild = ietRightChildIndex(index);// 檢查左子節點是否存在且大于當前最大值if (hasLeftChild(heap, index) && heap->arr[leftChild] > heap->arr[maxIndex]) {maxIndex = leftChild;}// 檢查右子節點是否存在且大于當前最大值if (hasRightChild(heap, index) && heap->arr[rightChild] > heap->arr[maxIndex]) {maxIndex = rightChild;}// 如果maxIndex不是當前index,說明子節點比父節點大,需要交換if (maxIndex != index) {swap(&heap->arr[index], &heap->arr[maxIndex]);heapifyDown(heap, maxIndex); // 遞歸向下調整}
}/*** @brief 插入一個元素到最大堆* 將新元素放到數組末尾,然后向上調整以維護堆性質。** @param heap 指向MaxHeap的指針* @param value 要插入的值* @return true 插入成功* @return false 堆已滿,插入失敗*/
bool insert(MaxHeap* heap, int value) {if (isFull(heap)) {printf("Error: Heap is full. Cannot insert %d.\n", value);return false;}heap->arr[heap->size] = value; // 將新元素放到數組末尾heap->size++;                 // 堆大小加1heapifyUp(heap, heap->size - 1); // 從新元素位置向上調整printf("Inserted: %d\n", value);return true;
}/*** @brief 從最大堆中刪除最大元素(根元素)* 將根元素與最后一個元素交換,然后刪除最后一個元素,最后向下調整新的根元素。** @param heap 指向MaxHeap的指針* @param deletedValue 用于存儲被刪除的值的指針* @return true 刪除成功* @return false 堆為空,刪除失敗*/
bool deleteMax(MaxHeap* heap, int* deletedValue) {if (isEmpty(heap)) {printf("Error: Heap is empty. Cannot delete.\n");return false;}*deletedValue = heap->arr[0]; // 存儲根元素的值heap->arr[0] = heap->arr[heap->size - 1]; // 將最后一個元素移動到根heap->size--;                            // 堆大小減1heapifyDown(heap, 0);                    // 從根節點向下調整printf("Deleted Max: %d\n", *deletedValue);return true;
}/*** @brief 查看最大堆的根元素(最大值)** @param heap 指向MaxHeap的指針* @param peekedValue 用于存儲查看到的值的指針* @return true 查看成功* @return false 堆為空,查看失敗*/
bool peekMax(MaxHeap* heap, int* peekedValue) {if (isEmpty(heap)) {printf("Error: Heap is empty. No max element.\n");return false;}*peekedValue = heap->arr[0];printf("Peeked Max: %d\n", *peekedValue);return true;
}/*** @brief 打印堆的當前元素(按數組順序)* 注意:這只是打印數組內容,不代表堆的樹形結構。*/
void printHeap(MaxHeap* heap) {printf("Heap elements (array order): [");for (int i = 0; i < heap->size; i++) {printf("%d", heap->arr[i]);if (i < heap->size - 1) {printf(", ");}}printf("] (Size: %d, Capacity: %d)\n", heap->size, heap->capacity);
}/*** @brief 將一個無序數組構建成一個最大堆* 從最后一個非葉子節點開始,依次向上調整每個節點。* 時間復雜度:O(n)** @param heap 指向MaxHeap的指針* @param arr 待構建的原始數組* @param size 原始數組的大小* @return true 成功構建* @return false 原始數組大小超過堆容量*/
bool buildHeap(MaxHeap* heap, int* arr, int size) {if (size > heap->capacity) {printf("Error: Array size (%d) exceeds heap capacity (%d).\n", size, heap->capacity);return false;}for (int i = 0; i < size; i++) {heap->arr[i] = arr[i];}heap->size = size;// 從最后一個非葉子節點開始向上調整// 最后一個非葉子節點的索引是 (size / 2) - 1for (int i = (heap->size / 2) - 1; i >= 0; i--) {heapifyDown(heap, i);}printf("Heap built from array. \n");return true;
}// -----------------------------------------------------------
// 4. 主函數進行測試
// -----------------------------------------------------------
int main() {MaxHeap* myHeap = createMaxHeap(10); // 創建一個容量為10的堆// 插入操作測試insert(myHeap, 30);insert(myHeap, 10);insert(myHeap, 50);insert(myHeap, 20);insert(myHeap, 40);printHeap(myHeap); // 期望: [50, 40, 30, 10, 20] (或其他有效堆排列)// 查看最大值測試int maxVal;peekMax(myHeap, &maxVal); // 期望: 50// 刪除操作測試deleteMax(myHeap, &maxVal); // 期望: 刪除 50printHeap(myHeap); // 期望: [40, 20, 30, 10] (或其他有效堆排列)deleteMax(myHeap, &maxVal); // 期望: 刪除 40printHeap(myHeap); // 期望: [30, 20, 10] (或其他有效堆排列)// 插入更多元素直到滿insert(myHeap, 60);insert(myHeap, 70);insert(myHeap, 5);insert(myHeap, 90);insert(myHeap, 80);insert(myHeap, 100);insert(myHeap, 15);printHeap(myHeap); // 期望: 堆滿,10個元素// 嘗試插入超出容量insert(myHeap, 101); // 期望: 插入失敗提示// 從數組建堆測試MaxHeap* anotherHeap = createMaxHeap(8);int arr[] = {3, 9, 2, 8, 1, 6, 7, 5};int n = sizeof(arr) / sizeof(arr[0]);printf("\nBuilding heap from array: ");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");buildHeap(anotherHeap, arr, n);printHeap(anotherHeap); // 期望: [9, 8, 7, 5, 1, 6, 2, 3] (或其他有效堆排列)deleteMax(anotherHeap, &maxVal);printHeap(anotherHeap);// 釋放內存destroyMaxHeap(myHeap);destroyMaxHeap(anotherHeap);return 0;
}

代碼解釋:

  1. MaxHeap?結構體:

    • arr: 指向存儲堆元素的動態數組。

    • capacity: 數組的最大容量。

    • size: 堆中當前元素的數量,也是下一個要插入元素的索引。

  2. 輔助函數:

    • swap(): 簡單的值交換函數。

    • getParentIndex(),?getLeftChildIndex(),?ietRightChildIndex(): 根據數組索引計算父子節點索引。

    • hasLeftChild(),?hasRightChild(): 判斷一個節點是否有對應的子節點,防止越界。

    • isEmpty(),?isFull(): 檢查堆的狀態。

  3. 核心堆操作:

    • createMaxHeap()?/?destroyMaxHeap():?堆的內存分配與釋放。

    • heapifyUp(index)?(向上調整):

      • 當新元素插入到堆的末尾時,它可能比其父節點大,違反了最大堆性質。

      • heapifyUp?將該元素與其父節點比較,如果大于父節點則交換位置。

      • 這個過程持續向上,直到元素回到正確位置(不大于父節點)或到達根節點。

      • 時間復雜度:?O(log N),N 是堆的大小(因為每次操作向上移動一層)。

    • heapifyDown(index)?(向下調整):

      • 當刪除根節點(最大元素)后,或在建堆過程中,根節點被替換為最后一個元素,可能比其子節點小。

      • heapifyDown?將當前節點與其左右子節點比較,找出最大的子節點。

      • 如果當前節點小于最大的子節點,則與最大的子節點交換位置,然后對交換后的子節點位置遞歸調用?heapifyDown

      • 這個過程持續向下,直到元素回到正確位置(不小于任何子節點)或到達葉子節點。

      • 時間復雜度:?O(log N)。

    • insert(value):

      • 將新元素添加到數組的末尾(heap->arr[heap->size])。

      • heap->size?增加。

      • 調用?heapifyUp?從新元素的位置向上調整,恢復堆性質。

      • 時間復雜度:?O(log N)。

    • deleteMax(deletedValue):

      • 如果堆為空,返回失敗。

      • 保存根節點的值(heap->arr[0])作為返回值。

      • 將數組的最后一個元素移動到根節點位置(heap->arr[0] = heap->arr[heap->size - 1])。

      • heap->size?減少。

      • 調用?heapifyDown?從根節點位置向下調整,恢復堆性質。

      • 時間復雜度:?O(log N)。

    • peekMax():?返回根元素的值,不刪除。

    • buildHeap(arr, size)?(建堆):

      • 將給定數組的元素直接復制到堆的內部數組中。

      • 最后一個非葉子節點開始(索引為?(size / 2) - 1),向上遍歷到根節點。

      • 對每個非葉子節點調用?heapifyDown,確保其子樹滿足堆性質。

      • 時間復雜度:?O(N)。這比 N 次?insert?操作(O(N log N))更高效。

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

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

相關文章

【前后前】導入Excel文件閉環模型:Vue3前端上傳Excel文件,【Java后端接收、解析、返回數據】,Vue3前端接收展示數據

【前后前】導入Excel文件閉環模型&#xff1a;Vue3前端上傳Excel文件&#xff0c;【Java后端接收、解析、返回數據】&#xff0c;Vue3前端接收展示數據 一、Vue3前端上傳&#xff08;導入&#xff09;Excel文件 ReagentInDialog.vue <script setup lang"ts" na…

網絡基礎入門:從OSI模型到TCP/IP協議詳解

網絡基礎入門&#xff1a;從OSI模型到TCP/IP協議詳解 一、網絡基礎概念與OSI七層模型 1.1 網絡通信的本質 計算機網絡的核心是將抽象語言轉換為二進制數據進行傳輸與計算&#xff0c;這一過程涉及多層抽象與轉換&#xff1a; 應用層&#xff1a;人機交互—抽象語言------編…

Linux致命漏洞CVE-2025-6018和CVE-2025-6019

Qualys 最近披露了兩個影響主流 Linux 發行版的本地權限提升 (LPE) 漏洞&#xff0c;分別是 CVE-2025-6018 和 CVE-2025-6019。這兩個漏洞可以被串聯利用&#xff0c;使得非特權用戶在幾秒鐘內獲得系統的 root 權限&#xff0c;從而實現對系統的完全控制。 一、漏洞詳情 這兩…

【Docker基礎】Docker鏡像管理:docker push詳解

目錄 引言 1 Docker鏡像推送基礎概念 1.1 什么是Docker鏡像推送 1.2 鏡像倉庫概述 1.3 鏡像標簽與版本控制 2 docker push命令詳解 2.1 基本語法 2.2 常用參數選項 2.3 實際命令示例 2.4 推送流程 2.5 步驟描述 3 鏡像推送實踐示例 3.1 登錄管理 3.2 標簽管理 3…

FPGA基礎 -- Verilog行為建模之循環語句

行為級建模&#xff08;Behavioral Modeling&#xff09;是 Verilog HDL 中最接近軟件編程語言的一種描述方式&#xff0c;適用于功能建模和仿真建模的初期階段。在行為級中&#xff0c;循環語句&#xff08;loop statements&#xff09;是常見且重要的控制結構&#xff0c;用于…

從C學C++(7)——static成員

從C學C(7)——static成員 若無特殊說明&#xff0c;本博客所執行的C標準均為C11. static成員和成員函數 對于特定類型的全體對象而言&#xff0c;有時候可能需要訪問一個全局的變量。比如說統計某種類型對象已創建的數量。 通常在C中使用全局變量來實現&#xff0c;如果我們…

大模型和ollama一起打包到一個docker鏡像中

如何將大模型鏡像和 Ollama 鏡像打包在一個 Docker 鏡像中 最近工作中有個需求是將ollama和大模型一起打成一個鏡像部署&#xff0c;將自己的操作步驟分享給大家。將大模型與 Ollama 服務打包在同一個 Docker 鏡像中&#xff0c;可以簡化部署流程并確保環境一致性。下面詳細介…

2025年滲透測試面試題總結-攻防研究員(應用安全)(題目+回答)

安全領域各種資源&#xff0c;學習文檔&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各種好玩的項目及好用的工具&#xff0c;歡迎關注。 目錄 攻防研究員(應用安全) 一、基礎部分 1. HTTP狀態碼對比 2. HTTP請求方法核心作用 3. 網絡分層協議速查表…

SpringBoot新聞項目學習day3--后臺權限的增刪改查以及權限管理分配

新增管理員修改管理員刪除管理員登錄 新增管理員 1.點擊新增按鈕打開一個對話框 2.確定新增對話框要顯示哪些內容 3.提交 4.后端處理、保存 5.響應前端 vue代碼 <template><!-- 新增代碼內容是比較多的,建議抽取出來,定義到一個獨立的vue文件中在列表組件中導入…

算法導論第二十五章 深度學習的倫理與社會影響

第二十五章 深度學習的倫理與社會影響 技術的光芒不應掩蓋倫理的陰影 隨著深度學習技術在各領域的廣泛應用&#xff0c;其引發的倫理和社會問題日益凸顯。本章將深入探討這些挑戰&#xff0c;并提供技術解決方案和最佳實踐&#xff0c;引導讀者構建負責任的人工智能系統。 25.…

Linux中ansible模塊補充和playbook講解

一、模塊使用 1.1 Yum模塊 功能&#xff1a;管理軟件包&#xff0c;只支持RHEL&#xff0c;CentOS&#xff0c;fedora&#xff0c;不支持Ubuntu其它版本 參數說明name要操作的軟件包名稱&#xff0c;支持通配符&#xff08;如 httpd, nginx*&#xff09;&#xff0c;也可以是…

唐代大模型:智能重構下的盛世文明圖譜

引言&#xff1a;當長安城遇見深度學習 一件唐代鎏金舞馬銜杯銀壺的虛擬復原品正通過全息投影技術演繹盛唐樂舞。這個跨越時空的場景&#xff0c;恰似唐代大模型技術的隱喻——以人工智能為紐帶&#xff0c;連接起長安城的盛世氣象與數字時代的文明重構。作為人工智能與歷史學…

國產ARM/RISCV與OpenHarmony物聯網項目(三)網關設備控制

一、設備控制界面與功能設計 程序界面運行與設計效果如下: 設備控制相關程序調用關系圖如下&#xff1a; 其中device_control.html程序為網頁界面顯示程序&#xff0c;led_alarm.cgi程序為光線數據的報警超限數據設置與管理&#xff0c;led_control.cgi程序功能為對Led燈的開…

微信小程序反編譯實戰教程

在實際滲透測試或安全分析中&#xff0c;經常會遇到微信小程序中的簽名加密&#xff08;sign&#xff09;機制&#xff0c;這些機制大多具備防重放、防篡改的特性&#xff0c;導致我們在抓包時難以直接復現請求。 &#x1f50d; 另一方面&#xff0c;一些小程序的代碼中往往會…

【NLP入門系列三】NLP文本嵌入(以Embedding和EmbeddingBag為例)

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 博主簡介&#xff1a;努力學習的22級本科生一枚 &#x1f31f;?&#xff1b;探索AI算法&#xff0c;C&#xff0c;go語言的世界&#xff1b;在迷茫中尋找光芒…

文心一言(ERNIE Bot):百度打造的知識增強大語言模型

1. 產品概述 文心一言&#xff08;ERNIE Bot&#xff09;是百度自主研發的知識增強大語言模型&#xff0c;于2023年3月16日正式發布&#xff0c;對標OpenAI的ChatGPT&#xff0c;具備文本生成、多模態交互、邏輯推理、中文理解等能力。該模型基于百度的飛槳深度學習平臺和文心…

Java-49 深入淺出 Tomcat 手寫 Tomcat 實現【02】HttpServlet Request RequestProcessor

點一下關注吧&#xff01;&#xff01;&#xff01;非常感謝&#xff01;&#xff01;持續更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持續更新中&#xff01;&#xff08;長期更新&#xff09; 目前2025年06月13日更新到&#xff1a; AI煉丹日志-28 - Aud…

在VB.net中,文本插入的幾個自定義函數

一、如果你是高手&#xff0c;一定“識貨”&#xff0c;分享給你 二、可應用于文本插入的幾種方式&#xff1a;6種 三、需要用到以下的幾個函數&#xff1a; 上代碼&#xff1a; Module TextModule <summary> 在指定位置插入文本 </summary> <p…

QC -io 服務器排查報錯方式/報錯: Failed to convert string to integer of varId variable!“

進斷點控制臺有報錯之后&#xff0c;復制報錯信息到 頭部菜單欄 1.編輯 -> 2.Find/Replace ->3.Advanced Find ->4. Project“xxxxx” 能找到問題點 再分析定位 在排查報錯時候&#xff0c;進入了這個報錯&#xff0c;msgInfo "MyTcpRedis: Failed to conver…

c++中auto與decltype使用

在 C11及后續版本中&#xff0c;關鍵字auto和decltype都是用于類型推導的&#xff0c;但它們的使用場景和行為有所不同。 1. auto 關鍵字 作用 auto 用于自動推導變量的類型&#xff0c;由編譯器根據初始化表達式來確定。 常見用法 // 基本用法 auto x 42; // int…