【數據結構與算法】從完全二叉樹到堆再到優先隊列

完全二叉樹 CBT

設二叉樹的深度為 h , 若非最底層的其他各層的節點數都達到最大個數 , 最底層 h 的所有節點都連續集中在左側的二叉樹叫做 完全二叉樹 .

特點

  1. 對任意節點 , 其右分支下的葉子節點的最底層為 L , 則其左分支下的葉子節點的最低層一定是 L 或 L + 1 .
  2. 完全二叉樹度為 1 的點只有 1 個或 0 個 .
  3. 按層序遍歷的順序訪問 , 度為 1 或 0 的節點的后續節點的度均為 0 .

二叉樹的層序遍歷

層序遍歷是一種廣度優先搜索 , 要借助 隊列 數據結構實現 , 核心邏輯如下 :

  1. 初始化隊列 , 把根節點加入隊列 .
  2. 從隊列取出一個節點 , 將該節點的左右節點加入隊列 , 重復處理至隊列為空 .
class TreeNode{int val;TreeNode left;TreeNode right;TreeNode(int val){this.val = val;}public static List<Integer> levelOrderTraversal(TreeNode root){Queue<TreeNode> queue = new LinkedList<>();List<Integer> list = new ArrayList<>();if (root == null) {return list;}queue.offer(root);while(!queue.isEmpty()){TreeNode node = queue.poll();list.add(node.val);if (node.left != null) {queue.offer(node.left);}if (node.right != null) {queue.offer(node.right);}}return list;}
}

Queue 的 offer 方法會將 null 加入隊列 , 因此我們要加入條件語句避免產生空指針異常 .

判斷二叉樹是否為完全二叉樹

因為完全二叉樹最底層 h 的所有節點都連續集中在左側 , 且按層序遍歷的順序訪問 , 度為 1 或 0 的節點的后續節點的度均為 0 ( 特點 3 ) , 判斷是否為完全二叉樹較常用的方法要借助 二叉樹的層序遍歷 , 核心邏輯如下 :

  1. 對二叉樹進行層序遍歷 , 使用隊列保存節點 .
  2. 遍歷過程中維護標識符 end , 其表示是否遇到過度為 1 或 0 的節點 .
  3. end 標識符翻轉后如果再次遇到有左右任一子節點的節點 , 直接返回 .
class TreeNode{int val;TreeNode left;TreeNode right;TreeNode(int val){this.val = val;}public static Boolean isCBTorNot(TreeNode root){Queue<TreeNode> queue = new LinkedList<>();if (root == null) {return true;}queue.offer(root);boolean end = false;while(!queue.isEmpty()){TreeNode node = queue.poll();if(node.left != null){if(end) return false;queue.offer(node.left);}else end = true;if(node.right != null){if(end) return false;queue.offer(node.right);}else end = true;}return true;}
}

完全二叉樹的數組表示

為什么可以使用數組表示 ?

完全二叉樹非最底層的其他各層的節點數都達到最大個數 , 最底層 h 的所有節點都連續集中在左側 , 使得使用數組存儲時能夠緊密排列 , 避免空間浪費 .

父子節點的索引關系

推導過程如圖 , 結論 :

  • 父節點的數組索引為 n , 則左子節點的數組索引為 2 * n + 1 , 右子節點的數組索引為 2 * n + 2 , 祖父節點的數組索引為 ( n - 1 ) / 2 .
    在這里插入圖片描述

堆 Heap

堆是滿足特定條件的完全二叉樹 , 主要分為 大頂堆小頂堆 兩種類型 , 大頂堆指 任意節點值 >= 其子節點值 , 小頂堆指 任意節點值 <= 其子節點值 .

堆化

堆化是將無序數組轉化為堆的過程 . 添加元素入堆時 :

  1. 給定元素 val , 我們首先將該元素添加到堆底 .
  2. val 可能大于堆中其他元素 , 此時堆被暫時破壞 , 我們要從堆底至頂進行堆化 .
  3. 比較 val 節點與其節點的值 , 若插入節點更大則交換二者 , 重復執行操作直到節點上升到根節點或其父節點更大時結束 .

初始化無序數組為堆時 :

  1. 對于每個非葉子節點執行下沉操作 : 比較該節點與左右子節點 , 若該節點值小于子節點最大值 , 則交換該節點與最大值子節點 .
  2. 重復操作直到節點下沉到葉子節點或該節點值大于或等于左右子節點值的最大值 .

堆的節點總數為 n , 樹的層數為 log n , 堆化操作的迭代次數至多為 log n , 知元素入堆操作的時間復雜度是 O(log n) .

class Heap {public static void heapify(int[] arr, int n, int i) {int largest = i;int left = 2 * i + 1;int right = 2 * i + 2;if (left < n && arr[left] > arr[largest]) largest = left;if (right < n && arr[right] > arr[largest]) largest = right;if (largest != i) {int swap = arr[i];arr[i] = arr[largest];arr[largest] = swap;heapify(arr, n, largest);}}public static void buildMaxHeap(int[] arr) {int n = arr.length;for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}}

堆排序

堆排序基于頂堆的特性 , 重復將堆頂元素與堆尾元素交換 , 堆化非隊尾元素的剩余元素 直至數組有序 . 堆的節點數為 n , 堆化操作的時間復雜度為 log n , 因此堆排序的時間復雜度是 O(n log n) , 堆排序基于原地交換 , 空間復雜度為常數級 , 是性能良好的排序方法 .

class Heap{public static void heapSort(int[] arr) {int n = arr.length;for (int i = n / 2 - 1; i >= 0; i--) {heapify(arr, n, i);}for (int i = n - 1; i > 0; i--) {int temp = arr[0];arr[0] = arr[i];arr[i] = temp;heapify(arr, i, 0);}}
}

優先隊列 PriorityQueue

優先隊列是特殊的隊列數據結構 , 在 Java PriorityQueue 類中 , 默認優先級為從小到大的自然排序 , 可以通過 lambda 表達式自定義比較器 Comparator 函數類型 .

PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(); // 默認為自然順序
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(Comparator.reverseOrder()); // 更改比較器為降序

如果要儲存自定義引用數據類型時 , 有兩種方式定義元素優先級 :

  1. 引用類實現 Comparable 接口 : 在類中實現 compareTo() 方法 .
  2. 在初始化 PriorityQueue 時傳入比較器對象 .
class Person{private String name;private int age;Person(String name, int age){this.name = name;this.age = age;}@Overridepublic int compareTo(Person person){retutn Integer.compare(this.age, person.age);}
}
public class Main{public static void main(String[] args){PriorityQueue<Person> pq = new PriorityQueue<>();// 或PriorityQueue<Person> pq = new PriorityQueue<>((p1, p2) -> Integer.compare(p2.getAge(), p1.getAge()));}
}

Java 優先隊列的方法與隊列類似 .

23. 合并 K 個升序鏈表

在這里插入圖片描述

我們維護一個按節點值自然排序的優先隊列 , 將鏈表數組內的所有非空數組加入隊列 , 每次出堆堆頂節點 , 定義返回節點指向出堆節點 , 入堆出堆節點的后繼節點 , 返回節點指針后移 , 重復操作直到堆為空即可 .

class Solution {public ListNode mergeKLists(ListNode[] lists) {PriorityQueue<ListNode> pq = new PriorityQueue<>((a, b) -> a.val - b.val);for (ListNode head : lists) {if (head != null) {pq.offer(head);}}ListNode dummy = new ListNode();ListNode cur = dummy;while (!pq.isEmpty()) {ListNode node = pq.poll();if (node.next != null) {pq.offer(node.next);}cur.next = node;cur = cur.next;}return dummy.next;}
}

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

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

相關文章

Leetcode:1. 兩數之和

題目 給定一個整數數組 nums 和一個整數目標值 target&#xff0c;請你在該數組中找出 和為目標值 target 的那 兩個 整數&#xff0c;并返回它們的數組下標。 你可以假設每種輸入只會對應一個答案&#xff0c;并且你不能使用兩次相同的元素。 你可以按任意順序返回答案。 示…

flume整合kafka

需求一&#xff1a; 啟動flume 啟動kafka消費者&#xff0c;驗證數據寫入成功 新增測試數據 需求二&#xff1a; 啟動Kafka生產者 啟動Flume 在生產者中寫入數據

Hbase集群管理與實踐

一、HBase集群搭建實戰 1.1 環境規劃建議 硬件配置基準(以10節點集群為例): 角色CPU內存磁盤網絡HMaster4核16GBSSD 200GB(系統盤)10GbpsRegionServer16核64GB124TB HDD(JBOD)25GbpsZooKeeper4核8GBSSD 500GB10Gbps1.2 關鍵配置項示例(hbase-site.xml) <configu…

STM32 開發 - stm32f10x.h 頭文件(內存映射、寄存器結構體與宏、寄存器位定義、實現點燈案例)

概述 STM32F10x.h 是 STM32F1 系列微控制器的核心頭文件&#xff0c;提供了所有外設寄存器的定義和內存映射 一、內存映射 #define PERIPH_BASE ((uint32_t)0x40000000)#define APB1PERIPH_BASE PERIPH_BASE #define APB2PERIPH_BASE (PERIPH_BASE 0x…

QEMU源碼全解析 —— 塊設備虛擬化(23)

接前一篇文章:QEMU源碼全解析 —— 塊設備虛擬化(22) 本文內容參考: 《趣談Linux操作系統》 —— 劉超,極客時間 《QEMU/KVM源碼解析與應用》 —— 李強,機械工業出版社 特此致謝! QEMU啟動過程中的塊設備虛擬化 上一回解析了qcow2格式對應的qcow2_open函數,本回解…

【PCB工藝】推挽電路及交越失真

推挽電路(Push-Pull Circuit) 推挽電路(Push-Pull Circuit) 是一種常用于功率放大、電機驅動、音頻放大等場合的電路結構,具有輸出對稱、效率高、失真小等優點。 什么是推挽電路? 推挽是指:由兩種極性相反的器件(如 NPN 和 PNP、NMOS 和 PMOS)交替導通,一個“推”電…

RD電子實驗記錄本選用貼士A-B-C

傳統的實驗記錄本&#xff0c;令人又愛又恨本 如何挑選電子實驗室記錄本&#xff08;ELN&#xff09;的品牌/服務商/供應商&#xff1f; 電子實驗記錄本&#xff0c;又名為ELN&#xff0c;Electronic lab notebook&#xff0c;enotebook&#xff0c;研發電子管理系統&#xf…

Qt實戰之將自定義插件(minGW)顯示到Qt Creator列表的方法

Qt以其強大的跨平臺特性和豐富的功能&#xff0c;成為眾多開發者構建圖形用戶界面&#xff08;GUI&#xff09;應用程序的首選框架。而在Qt開發的過程中&#xff0c;自定義插件能夠極大地拓展應用程序的功能邊界&#xff0c;讓開發者實現各種獨特的、個性化的交互效果。想象一下…

java基礎之枚舉和注解

枚舉 簡介 枚舉&#xff1a;enumeration&#xff0c;jdk1.5中引入的新特性&#xff0c;用于管理和使用常量 入門案例 第一步&#xff1a;定義枚舉&#xff0c;這里定義一個動物類&#xff0c;里面枚舉了多種動物 public enum AnimalEnum {CAT, // 貓DOG, // 狗PIG // …

2.3java運算符

運算符 1. 算術運算符 算術運算符用于執行基本的數學運算&#xff0c;像加、減、乘、除等。 運算符描述示例加法int a 5 3; // a 的值為 8-減法int b 5 - 3; // b 的值為 2*乘法int c 5 * 3; // c 的值為 15/除法int d 6 / 3; // d 的值為 2%取模&#xff08;取余&…

升級 Spring Boot CLI

&#x1f31f; 升級 Spring Boot CLI 1?? &#x1f504; 通過包管理器升級 使用對應包管理器命令&#xff08;如 brew upgrade&#xff09; 2?? &#x1f4e5; 手動安裝升級 遵循 標準安裝說明 注意更新 PATH 環境變量移除舊版本路徑 &#x1f517; 鏈接原文&#xff1a…

如何輕松將RS232轉為Profibus DP,提升PLC效率?

如何輕松將RS232轉為Profibus DP&#xff0c;提升PLC效率&#xff1f; 今天&#xff0c;我們就來聊聊一個工業自動化中常見的應用場景&#xff1a;如何通過興達易控RS232轉Profibus DP網關&#xff0c;實現流量泵與PLC&#xff08;可編程邏輯控制器&#xff09;的通信。這個話…

QT 連接數據庫操作(15)

文章目錄 一、本章說明二、QT連接云端數據庫實現2.1 ODBC軟件安裝及參數設置2.2 軟件代碼實現三、項目源碼文件一、本章說明 注:本節為【基于STM的環境監測系統(節點+云服務器存儲+QT界面設計)】項目第15篇文章,前面已經創建了監測軟件的登錄窗口,接下來我們將在主窗口實…

linux系統之----命令行參數和環境變量

一、命令行參數 1.main()函數的參數 在C語言中&#xff0c;main函數可以接收命令行參數&#xff0c;其標準形式為&#xff1a; int main(int argc, char *argv[]) {// 程序代碼return 0; } 這里我們解釋一下&#xff1a; argc&#xff1a;參數個數計數器&#xff08;Argum…

解析excel中的圖片

解析excel中的圖片 前言一、pom依賴二、使用步驟1.示例數據2.代碼如下&#xff08;示例&#xff09;&#xff1a; 總結 前言 初始化數據是&#xff0c;需要將excel中的數據解析并插入數據庫。 但是某幾列存放的是圖片&#xff0c;這時候怎么辦呢。 主要解決的是&#xff1a;獲…

Unity任務系統筆記

數據結構設計 任務基類包括的字段&#xff1a; string 任務內容&#xff1b; Transform 任務目的地&#xff1b; MyCharacter 任務開啟后要更新對話的NPC&#xff1b; MyTalkData 任務開啟后相關NPC要說的對話數據&#xff1b; 共同方法&#xff1a;開啟任務、完成任務。…

STM32的開發環境介紹

目錄 STM32軟件環境 Keil軟件在線安裝 其他軟件環境安裝 STM32開發的幾種方式 STM32寄存器版本和庫函數版本 標準外設庫的作用&#xff1a; STM32軟件環境 STM32 的集成開發環境&#xff08;IDE&#xff09;&#xff1a;編輯編譯軟件 常見的環境&#xff1a; (1)KEIL&a…

【特殊場景應對9】視頻簡歷的適用場景與風險分析

寫在最前 作為一個中古程序猿,我有很多自己想做的事情,比如埋頭苦干手搓一個低代碼數據庫設計平臺(目前只針對寫java的朋友),比如很喜歡幫身邊的朋友看看簡歷,講講面試技巧,畢竟工作這么多年,也做到過高管,有很多面人經歷,意見還算有用,大家基本都能拿到想要的offe…

Linux系統性能調優技巧分享

在數字化時代,Linux 系統以其開源、穩定、高效的特性,成為服務器、云計算、物聯網等領域的核心支撐。然而,隨著業務規模的擴大和負載的增加,系統性能問題逐漸凸顯。掌握 Linux 系統性能調優技巧,不僅能提升系統運行效率,還能降低運維成本。下面從多個方面介紹實用的性能調…

關于Code_流蘇:商務合作、產品開發、計算機科普、自媒體運營,一起見證科技與藝術的交融!

Code_流蘇 &#x1f33f; 名人說&#xff1a;路漫漫其修遠兮&#xff0c;吾將上下而求索。—— 屈原《離騷》 創作者&#xff1a;Code_流蘇(CSDN)&#xff08;一個喜歡古詩詞和編程的Coder&#x1f60a;&#xff09; &#x1f31f; 歡迎來到Code_流蘇的CSDN主頁 —— 與我一起&…