堆(Heap)優先級隊列(Priority Queue)

一、堆的概念

堆(Heap)是一種特殊的基于樹的數據結構,通常分為最大堆和最小堆兩種類型。它滿足堆屬性:對于最大堆,父節點的值總是大于或等于其子節點的值;而對于最小堆,父節點的值總是小于或等于其子節點的值。

數組實現:雖然堆是一個基于樹的結構,但它通常用數組來實現。對于位于數組索引i處的元素,其左孩子的索引是2*i + 1,右孩子的索引是2*i + 2,而其父節點的索引是(i-1)/2

將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆。

堆的性質: 堆中某個節點(孩子節點)的值總是不大于或不小于其父節點的值;

堆總是一棵完全二叉樹

2.堆得存儲方式

二叉樹層序遍歷存儲在數組中

1)堆的創建(大根堆)

向下調整法 (同理可創建小根堆,孩子中最小的與根節點交換)

?完全二叉樹從最后一棵子樹慢慢調整為堆,最后一課子樹根節點與左右孩子最大值交換(根節點<左/右孩子).然后記得遞歸調整堆結構被破壞的子樹

package org.example;import java.util.Arrays;public class TestHeap {private int[] elem;private int usedsize;public  TestHeap(int capacity){this.elem=new int[capacity];}//初始化堆public  void initHeap(int []array){for (int i = 0; i < array.length ; i++) {elem[i]=array[i];usedsize++;//數組堆元素個數}}public void display(){System.out.println(Arrays.toString(elem));}//創建堆//一棵子樹有孩子一定先有左孩子 左孩子(usedsize-1-1)/2->父親節點public void createHeap(){//從最后一棵子樹根節點按個遍歷每棵樹根節點for(int parent=((usedsize-1-1)/2);parent>=0;parent--){shiftDown(parent,usedsize);}}public void shiftDown(int parent,int usedsize){int child=(2*parent)+1;//左孩子節點下標while(child<usedsize){//要確保有右孩子且不越界if(child+1<usedsize&&elem[child]<elem[child+1]){//右孩子比左孩子大 找到孩子節點的最大值child++;}//child是左右孩子節點最大值的下標if(elem[child]>elem[parent]){//交換節點swap(child,parent);//父節點向下移,繼續調整parent=child;child=2*parent+1;}else {//已經是大根堆不需要調整break;}}}public void swap(int i,int j){int tmp=elem[i];elem[i]=elem[j];elem[j]=tmp;}}

Test測試類

package org.example;import java.util.Arrays;public class Test {public static void main(String[] args) {int []array={27,15,19,18,28,34,65,49,25,37};TestHeap testheap=new TestHeap(array.length);testheap.initHeap(array);testheap.createHeap();testheap.display();}
}

?

創建前

創建后(大根堆)

?2)堆的插入

向上調整(先將新插入的節點放到堆的末尾,最后一個孩子之后,堆的結構性質被破壞,在慢慢往上移,向上調整為大/小根堆)

注意:空間放滿時需要擴容

 public void offer(int val){if(isfull()){this.elem=Arrays.copyOf(elem,2*elem.length);//向上調整}//添加的元素是新的孩子this.elem[usedsize]=val;shiftUp(usedsize);usedsize++;}//向上調整函數 (也創建堆)//向下調整方法比向上調整方法少遍歷(最后)一層節點public void  shiftUp(int child){int parent=(child-1)/2;while (child>0){if(elem[child]>elem[parent]){swap(child,parent);child=parent;parent=(child-1)/2;}else {break;}}}public  boolean isfull(){return elem.length==usedsize;}

main方法

int []array={27,15,19,18,28,34,65,49,25,37};TestHeap testheap=new TestHeap(array.length);testheap.initHeap(array);testheap.createHeap();//testheap.display();testheap.offer(88);//插入元素打印testheap.display();

3)堆的刪除

刪除的一定是堆頂(根節點元素)

a.將堆頂元素與最后一個孩子元素交換

b.將堆中有效元素的個數減少一個

c.將堆頂元素向下調整,滿足堆特性為止

    public int pool(){//一定是刪除堆頂元素int tmp= elem[0];//保留刪除的元素//交換元素swap(0,usedsize-1);usedsize--;shiftDown(0,usedsize);//返回被刪除的元素return tmp;}
 int val=testheap.pool();System.out.println("被刪除的元素是"+val);//刪除后元素打印testheap.display();

    public void display(){// System.out.println(Arrays.toString(elem));//顯示堆的有效部分System.out.println(Arrays.toString(Arrays.copyOf(elem, usedsize)));}

打印方法display()修改原因

你遇到的問題是:

> 刪除堆頂元素(65)后,65 **仍然出現在數組中**,只是被“擠”到了最后面,數組并沒有真正縮小,65 依然存在。

---

## ? 你的代碼是**邏輯正確的**,但你對 `display()` 方法的理解有誤。

---

## 🧠 堆的底層實現是一個數組,但它**不是動態收縮的數組**,而是通過 `usedsize` 來控制堆的有效元素數量。

你調用 `pool()` 方法后:

```java
usedsize--;
```

這意味著堆的有效長度減少了 1,但底層數組并沒有變小,**只是堆不再“關心”最后一個元素**。

---

## 🔍 舉個例子

你原來的堆是:

```
[65, 49, 34, 25, 37, 27, 19, 18, 15, 28]
```

調用 `pool()` 后,堆會執行:

```java
swap(0, usedsize - 1); // 交換 65 和 28
usedsize--; ? ? ? ? ? ?// usedsize 從 10 變成 9
shiftDown(0, usedsize); // 從新的堆頂 28 開始下濾
```

此時數組變成:

```
[49, 37, 34, 25, 28, 27, 19, 18, 15, 65]
```

但是 `usedsize = 9`,意味著堆只“看到”前 9 個元素:

```
[49, 37, 34, 25, 28, 27, 19, 18, 15]
```

而 `65` 被留在數組最后,**只是不再屬于堆的范圍**。

---

## ? 所以你看到的輸出:

```
[49, 37, 34, 25, 28, 27, 19, 18, 15, 65]
```

這是完全正常的,因為:

- `display()` 方法打印的是整個數組(包括不屬于堆的部分)
- 你沒有重新拷貝數組或清空無效數據

---

## ? 如何正確打印堆的有效部分?

修改 `display()` 方法為只打印有效部分:

```java
public void display() {
System.out.println(Arrays.toString(Arrays.copyOf(elem, usedsize)));
}
```

## ? 修改后輸出:

調用 `testheap.display();` 會輸出:

```
49 37 34 25 28 27 19 18 15
```

二、優先級隊列

隊列是一種先進先出(FIFO)的數據結構,但有些情況下,操作的數據可能帶有優先級,一般出隊 列時,可能需要優先級高的元素先出隊列,該中場景下,使用隊列顯然不合適,比如:在手機上玩游戲的時候,如 果有來電,那么系統應該優先處理打進來的電話;

數據結構應該提供兩個最基本的操作,一個是返回最高優先級對象,一個是添加新的對象。這種數 據結構就是優先級隊列(Priority Queue)。

JDK1.8中的PriorityQueue底層使用了堆這種數據結構,而堆實際就是在完全二叉樹的基礎上進行了一些調整。

Java集合框架中提供了PriorityQueue和PriorityBlockingQueue兩種類型的優先級隊列,PriorityQueue是線 程不安全的,PriorityBlockingQueue是線程安全的

1)優先級隊列使用

導包

import java.util.PriorityQueue;

2. PriorityQueue中放置的元素必須要能夠比較大小,不能插入無法比較大小的對象,否則會拋出 ClassCastException異常

3. 不能插入null對象,否則會拋出NullPointerException

4. 沒有容量限制,可以插入任意多個元素,其內部可以自動擴容

5. 插入和刪除元素的時間復雜度為

6. PriorityQueue底層使用了堆數據結構

7. PriorityQueue默認情況下是小堆---即每次獲取到的元素都是最小的元素

public class Test {public static void main(String[] args) {PriorityQueue<Integer> priorityQueue=new PriorityQueue<>();priorityQueue.offer(10);priorityQueue.offer(4);priorityQueue.offer(8);priorityQueue.offer(3);priorityQueue.offer(7);System.out.println(priorityQueue.poll());System.out.println(priorityQueue.poll());System.out.println(priorityQueue.poll());System.out.println(priorityQueue.poll());System.out.println(priorityQueue.poll());}

打印結果證明是小根堆

?2)優先級隊列的創建方式(3種)

static void TestPriorityQueue(){
// 創建一個空的優先級隊列,底層默認容量是11
PriorityQueue<Integer> q1 = new PriorityQueue<>();
// 創建一個空的優先級隊列,底層的容量為initialCapacity
PriorityQueue<Integer> q2 = new PriorityQueue<>(100);
ArrayList<Integer> list = new ArrayList<>();
list.add(4);
list.add(3);
list.add(2);
list.add(1);
// 用ArrayList對象來構造一個優先級隊列的對象
// q3中已經包含了三個元素
PriorityQueue<Integer> q3 = new PriorityQueue<>(list);
System.out.println(q3.size());
System.out.println(q3.peek());
}

3)優先級隊列的使用方法

函數名? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 功能介紹

boolean offer(E e)? ? 插入元素e,插入成功返回true,如果e對象為空,拋出NullPointerException異常,時間復雜度 log2N,注意:空間不夠時候會進行擴容

E peek() 獲取優先級最高的元素,如果優先級隊列為空,返回null

E poll() 移除優先級最高的元素并返回,如果優先級隊列為空,返回null

int size() 獲取有效元素的個數

void clear() 清空

boolean isEmpty() 檢測優先級隊列是否為空,空返回true

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

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

相關文章

踩坑記錄:因版本不匹配導致 Boost 1.85 編譯失敗的完整解決過程

踩坑記錄&#xff1a;因版本不匹配導致 Boost 1.85 編譯失敗的完整解決過程 轉載請注明出處&#xff0c;歡迎評論區交流。 背景 最近在 Windows 11 VS2022 環境下嘗試用 b2 編譯 Boost 1.85.0&#xff0c;結果一路踩坑&#xff0c;最后發現罪魁禍首是 Boost.Build 自帶的 msv…

InfluxDB Line Protocol 協議深度剖析(二)

四、Line Protocol 寫入操作與實踐&#xff08;一&#xff09;HTTP API 寫入使用 HTTP API 是通過 Line Protocol 寫入數據到 InfluxDB 的常用方式。InfluxDB 1.x&#xff1a;請求方式為 POST&#xff0c;URL 格式為 “http://host:port/write?dbdatabase_name”。其中&#x…

負載均衡:提升業務性能的關鍵技術

一.負載均衡&#xff08;3.6 &#xff09;1.1.什么是負載均衡&#xff1a;負載均衡&#xff1a;Load Balance&#xff0c;簡稱LB&#xff0c;是一種服務或基于硬件設備等實現的高可用反向代理技術&#xff0c;負載均 衡將特定的業務(web服務、網絡流量等)分擔給指定的一個或多個…

【STM32項目】智能家居(版本1)

????大家好&#xff0c;這里是5132單片機畢設設計項目分享&#xff0c;今天給大家分享的是基于《基于STM32的智能家居設計》。 目錄 1、系統功能 2.1、硬件清單 2.2、功能介紹 2.3、控制模式 2、演示視頻和實物 3、系統設計框圖 4、軟件設計流程圖 5、原理圖 6、主…

OpenSCA開源社區每日安全漏洞及投毒情報資訊—2025年7月24日

2025年7月24日安全風險情報資訊在野漏洞風險&#xff08;CVE未收錄&#xff09;&#xff1a;1公開漏洞精選&#xff1a;2組件投毒情報&#xff1a;2在野漏洞風險&#xff08;CVE未收錄&#xff09;1.1 gemini-cli項目潛在命令注入漏洞項目詳情項目描述&#xff1a;gemini-cli是…

飛算 JavaAI 深度實戰:從老項目重構到全棧開發的降本增效密碼

飛算 JavaAI 深度實戰&#xff1a;從老項目重構到全棧開發的降本增效密碼引言正文一、智能引導模塊&#xff1a;老項目重構的 “手術刀” 級解決方案1.1 本地化智能分析&#xff1a;IDEA 插件實操演示1.1.1 &#x1f4cc; IDEA 插件安裝步驟1.1.1.1 首先打開idea工具&#xff0…

分布式推客系統開發全解:微服務拆分、傭金結算與風控設計

一、推客系統概述與市場背景推客系統&#xff08;也稱為分銷系統或社交電商系統&#xff09;已成為現代電商平臺和內容平臺的重要增長引擎。根據最新統計數據&#xff0c;2023年社交電商市場規模已突破3萬億元&#xff0c;占整體電商市場份額的25%以上。推客系統的核心價值在于…

Linux tcpdump 抓取udp 報文

一、tcpdump 支持命令選項tcpdump -i # 指定監聽網絡接口tcpdump -w # 將捕獲到的信息保存到文件中&#xff0c;且不分析和打印在屏幕tcpdump -r # 從文件中讀取數據tcpdump -n # 不把 ip 轉化成域名tcpdump -t # 在每行的輸出中不顯示時間tcpdump -v # 產生詳細的輸出tc…

Oracle數據塊8KB、OS默認認塊管理4KB,是否需調整大小為一致?

上班路上&#xff0c;腦中忽然閃現一個問題&#xff1a;Oracle數據庫塊大小&#xff08;8KB&#xff09;、操作系統文件系統塊大小&#xff08;4KB&#xff09;&#xff0c;為了減少IOPS&#xff0c;需不需要調整為一致&#xff1f;在數據塊保持一致的情況下&#xff0c;針對頻…

卡爾曼濾波器噪聲方差設置對性能影響的仿真研究

卡爾曼濾波器噪聲方差設置對性能影響的仿真研究 前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家,覺得好請收藏。點擊跳轉到網站。 1. 引言 卡爾曼濾波器是一種廣泛應用于信號處理、控制系統、導航系統等領域的遞歸估計算法。它通過對系…

“多線程修路:當count++變成災難現場”

1.現象 當我們操作一個線程池的時候&#xff0c;可能需要去計數&#xff0c;也就是統計count&#xff0c;那我們這里有一個疑問&#xff0c;會不會產生線程安全問題&#xff1f; 毫無疑問絕對會有線程安全問題。在線程池環境中&#xff0c;多個線程并發訪問和修改一個共享的 co…

GaussDB null的用法

1 null的定義null 空值代表丟失的未知數據。 默認情況下&#xff0c;表列可以保存 null 值。 本章解釋 is null 和 is not null 操作符。2 null值的贅述如果表中的列是可選的&#xff0c;那么我們可以插入一個新記錄或更新一個現有記錄&#xff0c;而無 需向列添加一個值。這意…

智慧農業新圖景:物聯網如何精準守護作物生長?

在傳統農業生產模式下&#xff0c;農民往往憑借經驗判斷作物生長需求&#xff0c;灌溉、施肥缺乏精準性&#xff0c;導致水資源浪費、土壤板結、作物產量與品質難以提升等問題。加之氣候變化無常&#xff0c;極端天氣頻發&#xff0c;給農業生產帶來諸多不確定性&#xff0c;傳…

[ComfyUI] -入門2- 小白零基礎搭建ComfyUI圖像生成環境教程

AI圖像生成已經成為AIGC(人工智能生成內容)領域的重要組成部分,而ComfyUI作為一款可視化的Stable Diffusion工作流工具,以其模塊化、高度自由化的特點吸引了越來越多創作者的關注。本文將手把手教你如何在Windows系統下,從零搭建屬于自己的ComfyUI圖像生成環境。 一、Comf…

java設計模式 -【單例模式】

單例模式的定義 單例模式&#xff08;Singleton Pattern&#xff09;是一種創建型設計模式&#xff0c;確保一個類只有一個實例&#xff0c;并提供一個全局訪問點。常用于需要控制資源或共享狀態的場景&#xff0c;例如數據庫連接、日志記錄器等 單例模式的實現方式 餓漢式&…

Flink 自定義類加載器和子優先類加載策略

子類優先加載Flink 默認采用了子優先&#xff08;Child-First&#xff09;的類加載策略來加載用戶代碼&#xff0c;以解決潛在的依賴沖突問題。我們可以通過源碼來證明這一點。ChildFirstClassLoader 的實現Flink 中負責實現“子優先”加載邏輯的核心類是 ChildFirstClassLoade…

Nginx 安全加固:如何阻止 IP 直接訪問,只允許域名訪問

在部署網站或 Web 應用時,我們通常會通過域名來訪問服務。然而,有時用戶可能會嘗試直接使用服務器的 IP 地址來訪問,這不僅可能繞過我們的域名特定配置(如 SSL 證書、重定向規則等),還可能導致不必要的安全風險或管理混亂。本文將介紹如何配置 Nginx,使其在通過 IP 地址…

服務端處于 TIME_WAIT 狀態的 TCP 連接,收到相同四元組的 SYN 后會發生什么?詳解

文章目錄一、先判斷 SYN 是否合法1、開啟「時間戳」機制1.1、合法 SYN1.2、非法 SYN2、關閉「時間戳」機制1.1、合法 SYN1.2、非法 SYN二、收到合法 SYN三、收到非法 SYN一、先判斷 SYN 是否合法 1、開啟「時間戳」機制 1.1、合法 SYN 客戶端的 SYN「序列號」比服務端「期望…

數字化轉型:一文讀懂從單系統到智能架構(業務、應用、數據、技術架構)的跨越

在數字化浪潮席卷全球的今天&#xff0c;企業正經歷從 “單系統孤島” 到 “智能架構協同” 的范式革命。智能架構以業務敏捷化、應用服務化、數據價值化、技術云原生化為核心特征&#xff0c;通過四個維度的架構升級&#xff0c;破解傳統 IT 系統的效率瓶頸&#xff0c;支撐企…

AUTOSAR進階圖解==>AUTOSAR_SRS_Transformer

AUTOSAR Transformer 詳解 基于AUTOSAR 4.4.0標準的Transformer模塊分析與說明目錄 1. Transformer概述 1.1 Transformer的作用1.2 Transformer的基本特性 2. Transformer架構 2.1 整體架構2.2 類層次結構 3. Transformer類型 3.1 SOME/IP Transformer3.2 COM Based Transform…