解密完全二叉樹順序存儲之堆結構

前言:各位老鐵好,在前面博客中,筆者分享了有關二叉樹的博客,在那篇博客中,筆者講到了完全二叉樹的存儲結構中有兩種存儲方式,一種是順序存儲,一種是鏈式存儲,鏈式存儲筆者已經帶各位老鐵實現過了,今天筆者要分享的是,完全二叉樹的順序存儲之堆。

1.堆的定義

那么什么是堆呢?我們肯定知道堆是一個完全二叉樹,但是堆還需要滿足一個條件,那就是每一個父節點的值必須大于等于其所有子節點的值(大根堆),又或者是每一個父節點的值小于等于其所有子節點的值(小根堆)
堆的圖示
在這里插入圖片描述
這里提一嘴,堆的實際的存儲結構是數組來存儲,我們一般把堆的邏輯結構看成是特殊的完全二叉樹!!!

那么講完了什么是堆,接下來就到堆實現的重頭戲了。
再將堆結構的實現之前,我們先來鋪墊兩個概念
一個是向下調整算法,一個是向上調整算法

2.向下調整算法:向下調算法是有其中某一個非葉子節點不滿足堆的性質,從而將它進行向下不斷地調整到葉子節點,直到該節點滿足堆地性質,向下調整算法有一個前提,該節點地左右子樹必須滿足堆的性質

可能有老鐵看完筆者文字描述向下調整算法,對向下調整算法還不清楚,沒關系,筆者接下來會舉例加畫圖來給各位老鐵講解。
在這里插入圖片描述
基于上面的內容,相信各位老鐵一定懂得了向下調整算法了,那么接下來我們就需要來實現出向下調整算法,畢竟光說不練那是假把式嘛!(這里實現的是小根堆的向下調整算法)
我們知道堆的實際存儲結構是數組,那么我們需要給堆傳入一個數組,還需要有一個非葉子節點,因為我們是對非葉子節點進行調整嘛。(現在假設這個節點是根節點)
先定義一個結構體來表示堆(c語言實現,默認堆存儲的整形數據)

#include <stdio.h>
typedef struct Heap
{int* _data;//存放數據int _size;//堆里面元素個數int _capacity;//整個空間大小
}Heap;

那么接下來就是實現向下調整算法了

typedef struct Heap
{int* _data;//存放數據int _size;//堆里面元素個數int _capacity;//整個空間大小
}Heap;//交換兩個節點值
void Swap(int* h1, int* h2)
{int tmp=0;tmp = *h1;*h1 = *h2;*h2 = tmp;
}//n表示堆元素個數
void AdjustDowns(int* a, int n, int root)
{int parent = root;int child = parent * 2 + 1;while (child < n)//只要chlid節點還在堆范圍內,就繼續判斷{//將parent節點和左右子節點進行比較if (child + 1 < n && a[child + 1] < a[child])//右子節點小于左子節點就++chlid{++child;}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);//交換兩個節點//更新chlid和parentparent = child;child = parent * 2 + 1;}//比父節點大不需要交換else{break;}}
}

3.向上調整算法:從某個節點開始,不斷與父節點比較和交換,直到該節點滿足堆的性質或者到達根節點為止,也需要滿足除了這個節點的位置,前面的位置已經構成堆了。

筆者認為這個文字描述老鐵們應該能看的懂了,所以筆者就不再畫圖了。
直接來實現向上調整算法了

//向上調整算法
void AdjustUp(int* a, int child)
{//先算出parent節點位置int parent = (child - 1) / 2;//減一是節點下標從0開始while (child > 0)//若該節點不是根節點就繼續調整{if (a[child] < a[parent]){Swap(&a[child], &a[parent]);//更新child和parentchild = parent;parent = (child - 1) / 2;}else{break;}}
}

到這里我們就已經懂得了向上和向下調整算法了,下面我們就開始真正實現堆了。

4.初始化堆

void HeapInit(Heap* php)
{assert(php);//確保傳入的對象不為空php->a = NULL;php->capacity = php->size = 0;
}

5.堆的插入

我們要明白堆的插入分為幾種情況,是不是分為兩種情況,一種是當存放數據的數組滿了,是不是需要擴容才能插入,另一種是數組空間還足夠,可以直接插入。

//堆的末尾插入數據
void HeapPush(Heap* php, int x)
{assert(php);//空間滿了(按兩倍來擴容)if (php->size == php->capacity){int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;int* tmp = (int*)realloc(php->a, sizeof(int) * newcapacity);if (tmp < 0){perror("realloc fail");//perror打印錯誤信息}php->a = tmp;php->capacity = newcapacity;}//插入數據php->a[php->size] = x;php->size++;//進行向上調整AdjustUp(php->a, php->size - 1);
}

6.取堆頂的數據

這個很簡單,直接上代碼了

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

7.刪除堆頂數據

如果我們直接刪除堆頂的數據,是不是會破壞完全二叉樹的結構,那么我們就換一種思路,通過將最后一個節點和第一個節點進行交換,然后在刪除最后一個節點,最后再將第一個節點進行向下調整,這樣就保持住了完全二叉樹的結構。

void HeapPop(Heap* php)
{assert(php);assert(php->size > 0);//交換堆頂元素和最后一個元素Swap(&php->a[0], &php->a[php->size - 1]);//刪除最后一個元素php->size--;//最后將第一個節點向下調整AdjustDown(php->a, php->size, 0);
}

8.判斷堆是否為空

這個也很簡單,直接上代碼

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

9.釋放堆

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

10.有關于堆常考的一個面試題:Topk問題

Topk問題:假設現在有n個數,需要讓你選出最小的前k個數,你該如何選?

法一:使用堆排序,先建立一個大堆(建堆時間復雜度是0(n)),再讓堆頂元素和最后一個元素進行交換,我們知道堆頂元素是當前的最大值,那么每一次交換到當前的最后一個元素,就確保了當前堆頂元素放到該放的位置,進行n-1次進行交換,那么就整個有序了,那么總的時間復雜度是o(n*logn)

法二:我們可以先建立有k個元素的最大堆(堆頂是當前k個元素中的最大值),那么現在遍歷剩下的n-k個元素,如果比堆頂元素小,那么就替換堆頂,然后再調整,直到替換出最小的前k個元素。

法三:假設現在n=10億,我的內存肯定是存不下了,那么就不能直接使用堆排序了,那么這些值現在在文件中,那么我們該如何找出前k個最小元素

答:也是通過建立k個元素的大堆,再拿10億-k的數據和堆頂數據比較,比堆頂小就替換堆頂并調整,最后一直比較完10億-k個數據,就能找到最小的前k個元素了,那么我的內存僅僅需要維護o(k)

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

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

相關文章

通過針刺!鵬輝能源移動電源電池革新之作 Secu 系列:不燃電解液加持,充電寶安全新選擇

9月11日&#xff0c;鵬輝能源對外發布新一代移動電源高安全電池Secu系列。該產品通過采用不燃的電解液破解移動電源產品安全難題&#xff0c;直擊當下移動電源安全事故頻發的行業痛點&#xff0c;為移動電源行業帶來更安全、更可靠的半固態電池解決方案。數字化時代&#xff0c…

軟件定義汽車(SDV)與區域電子電氣架構(Zonal EEA)的技術革新

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 做到欲望極簡,了解自己的真實欲望,不受外在潮流的影響,不盲從,不跟風。把自己的精力全部用在自己。一是去掉多余,凡事找規律,基礎是誠信;二是…

在 Docker Compose 中解決文件權限不足的問題

在使用 Docker 和 Docker Compose 構建應用時&#xff0c;由于容器中的文件權限不足而導致某些容器可能無法訪問宿主機上的文件&#xff0c;或者容器內的文件系統無法正確讀取或寫入文件。問題描述在我的項目中&#xff0c;我使用 Docker Compose 來啟動多個服務&#xff0c;并…

認知語義學對人工智能自然語言處理的深層語義分析:理論啟示與實踐路徑

摘要隨著人工智能&#xff08;AI&#xff09;技術的飛速發展&#xff0c;自然語言處理&#xff08;NLP&#xff09;已成為其核心驅動力之一。然而&#xff0c;盡管以大型語言模型&#xff08;LLMs&#xff09;為代表的現代NLP系統在處理語言任務上取得了前所未有的成功&#xf…

React19 中的交互操作

需要安裝的庫 antd-mobile、use-immer在App.jsx 中引入組件 Actionimport "./App.css" import Action from "./pages/action" function App() {return (<><Action></Action></>) }export default Appaction.jsx 組件import LearnI…

倉頡編程語言青少年基礎教程:數組類型

倉頡編程語言青少年基礎教程&#xff1a;數組類型 數組本質上是有序、同類型數據的集合容器&#xff0c;其核心作用是高效組織、訪問和處理批量數據&#xff0c;同時結合語言特性&#xff0c;為開發者提供簡潔、高性能的數據管理方式。例如&#xff1a; main() { let v1: …

C++微基礎藍橋杯之旅9.9-9.12

這里主要還是強制類型轉換的使用//打印字符ASCII碼值 //輸入一個除空格以外的可見字符 //輸出其ASCII值--十進制整數 #include <iostream> using namespace std;int main() {char ch;cin >> ch;//字符cout << (int)ch << endl; return 0; }//打印字符…

邏輯漏洞(上)- 突破功能限制漏洞、用戶信息泄露(邏輯漏洞入門)

漏洞介紹&#xff1a; 在網絡攻防實戰中&#xff0c;常會遇到各種前端限制&#xff0c;繞過限制的方法大多是改包或者修改前端代碼來實現的。 漏洞環境&#xff1a;docker docker-compose up -d 啟動環境后&#xff1a;訪問 http://127.0.0.1:8983/web/# 發現查詢按鈕是無法使用…

tsv文件簡介

初步了解tsv文件在很多 OCR&#xff08;光學字符識別&#xff09;項目中&#xff0c;.tsv文件是標準的訓練數據標注文件&#xff0c;主要用于存儲 “圖像路徑 - 對應文本標簽” 的映射關系&#xff0c;同時可能包含圖像尺寸、文本長度等輔助信息&#xff0c;方便模型讀取訓練數…

apache poi 導出復雜的excel表格

如何導出復雜的excel 表格 如圖表格&#xff0c;存在行和列的合并&#xff0c;邊框&#xff0c;樣式&#xff0c;顏色等。依賴<!-- https://mvnrepository.com/artifact/org.apache.poi/poi --><dependency><groupId>org.apache.poi</groupId><arti…

下載 Eclipse Temurin 的 OpenJDK 提示 “無法訪問此網站 github.com 的響應時間過長”

打開 Eclipse Temurin 的 OpenJDK 的官網下載地址&#xff1a; https://adoptium.net/zh-CN/temurin/releases 問 deepseek&#xff1a; 國內網絡&#xff0c;打不開github.com網頁&#xff0c;提示github.com 的響應時間過長。 國內無法訪問 GitHub 或訪問緩慢&#xff0c;通…

C/C++類型轉換

C/C類型轉換 1. C類型轉換 C 語言中的類型轉換主要分為兩種&#xff1a;隱式類型轉換 (Implicit Conversion) - 由編譯器自動完成。顯式類型轉換 (Explicit Conversion) - 由程序員強制指定&#xff0c;也稱為強制類型轉換。1.2 隱式類型轉換 編譯器在編譯時自動進行的轉換&…

【Java】Windows切換Java8和Java11

現在有些項目要升級到Java17, 所以需要切換不同的java版本。 如何安裝Java8 由于已經安裝了jJava8, 之前的安裝文章&#xff1a;【Java】jdk8安裝——英文版 如何安裝Java17 Java17下載地址 https://www.oracle.com/java/technologies/downloads/#java17-windows 下載到電…

SQLite 數據庫核心知識與 C 語言編程

一、數據庫基礎概念1.1 數據庫分類根據規模和應用場景&#xff0c;數據庫可分為以下幾類&#xff1a;大型數據庫&#xff1a;Oracle&#xff08;適用于企業級高并發、大容量場景&#xff09;中型數據庫&#xff1a;MySQL、MSSQL&#xff08;適用于中小型系統、Web 應用&#xf…

Netty 調優篇:實戰配置、性能監控與常見坑

&#x1f680; Netty 調優篇&#xff1a;實戰配置、性能監控與常見坑前面我們已經深入了 Netty 的 線程模型、Pipeline、EventLoop、內存池、零拷貝和背壓機制。 但在實際工作中&#xff0c;很多人踩坑的地方不是“源碼沒看懂”&#xff0c;而是 調優沒做好。 今天我們就從三個…

Linux Node.js 安裝及環境配置詳細教程

如果您喜歡此文章&#xff0c;請收藏、點贊、評論&#xff0c;謝謝&#xff0c;祝您快樂每一天。 一、Node.js是什么 Node.js是一個基于Chrome V8引擎的[JavaScript運行環境]。 Node.js使用了一個事件驅動、非阻塞式I/O 的模型。 Node.js是一個讓JavaScript運行在服務端的開…

呼叫中心系統IVR流程設計的心理學

呼叫中心的 IVR&#xff08;交互式語音應答&#xff09;系統看似是 “機器與用戶的對話”&#xff0c;實則暗藏對用戶心理的精準把握。其設計需圍繞降低焦慮、提升效率、強化信任三大核心目標&#xff0c;背后依托認知心理學、行為心理學、情感心理學等理論支撐。一、認知負荷理…

一些開源或免費的網絡管理工具

整理開源及免費網絡管理工具推薦,涵蓋監控、配置、安全、流量分析等場景,適用于不同規模的網絡環境: ?一、網絡監控與性能分析? 1. ?Zabbix? ?特點?:企業級監控方案,支持SNMP、IPMI、JMX等多種協議,提供實時儀表盤、告警通知和自動化發現功能。 ?適用場景?:服…

谷粒商城項目-P16快速開發-人人開源搭建后臺管理系統

1.對腳手架工程進行改造 此項目選用的腳手架工程是人人開源 地址&#xff1a;人人開源 選擇的是下圖標紅的renren-fast作為后端&#xff0c;renren-fast-vue作為前端 克隆上述兩個項目 2.后端改造 2.1將renrenfast項目的git文件夾刪除后&#xff0c;拖進后端代碼文件夾中 2…

V少JS基礎班之第八彈:this

文章目錄一、 前言二、本節涉及知識點三、重點內容1、從新的角度認識this2、this是函數的參數3、this的值4、函數的調用1- 裸函數調用2- 函數作為構造函數調用3- 函數作為對象的方法調用4- 函數顯示調用5- 箭頭函數一、 前言 第八彈內容是this。this相對來說難度不大&#xff…