二叉樹理論基礎

二叉樹種類

滿二叉樹:每個非葉子節點都有且只有兩個子節點。

和完全二叉樹:除了最底層外,其他各層都是滿的;最底層的節點都集中在左側。

二叉搜索樹:對于任意節點 u,左子樹上所有節

點的值都小于 u.val,右子樹上所有節點的值都大于 u.val

平衡二叉樹任意節點的左右子樹高度差 ≤ 1。

二叉樹的存儲方式

「二叉樹可以鏈式存儲,也可以順序存儲。」

那么鏈式存儲方式就用指針, 順序存儲的方式就是用數組。

遍歷算法

  1. 深度優先遍歷(DFS)

    • 前序(Pre?Order):根 → 左 → 右

      /*** Definition for a binary tree node.* public class TreeNode {*     int val;*     TreeNode left;*     TreeNode right;*     TreeNode() {}*     TreeNode(int val) { this.val = val; }*     TreeNode(int val, TreeNode left, TreeNode right) {*         this.val = val;*         this.left = left;*         this.right = right;*     }* }*/
      class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res = new LinkedList<>();preorder(root, res);return res;}void preorder(TreeNode root,List<Integer> list){if(root == null){return;}list.add(root.val);preorder(root.left,list);        preorder(root.right,list);}
      }
    • 中序(In?Order):左 → 根 → 右

      
      class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res = new LinkedList<>();inorder(root, res);return res;}void inorder(TreeNode root,List<Integer> list){if(root == null){return;}inorder(root.left,list);list.add(root.val);inorder(root.right,list);}
      }
    • 后序(Post?Order):左 → 右 → 根

      
      class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res = new LinkedList<>();inorder(root, res);return res;}void inorder(TreeNode root,List<Integer> list){if(root == null){return;}           inorder(root.left,list);        inorder(root.right,list);list.add(root.val);}
      }
  2. 廣度優先遍歷(BFS)/層序(Level?Order)

    • 按層自上而下、同層從左到右依次訪問,通常用隊列實現。

?二叉樹的定義

public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) { this.val = val; }TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}
}

遞歸寫法?

  1. 確定遞歸函數的參數和返回值:?確定哪些參數是遞歸的過程中需要處理的,那么就在遞歸函數里加上這個參數, 并且還要明確每次遞歸的返回值是什么進而確定遞歸函數的返回類型。

  2. 確定終止條件:?寫完了遞歸算法, 運行的時候,經常會遇到棧溢出的錯誤,就是沒寫終止條件或者終止條件寫的不對,操作系統也是用一個棧的結構來保存每一層遞歸的信息,如果遞歸沒有終止,操作系統的內存棧必然就會溢出。

  3. 確定單層遞歸的邏輯:?確定每一層遞歸需要處理的信息。在這里也就會重復調用自己來實現遞歸的過程。

以前序遍歷為例:

1.首先,確認參數,因為我們訪問的是節點,所以參數要包含節點對象,其次要返回訪問的數值,所以也要包含一個list對象保存訪問的值

2.每一層遞歸的終止條件就是遇見空節點,所以判斷當前節點是否為空,為空就返回

3.?前序遍歷是中左右的順序,所以在單層遞歸的邏輯,是要先取中節點的數值,代碼如下:

迭代解決遍歷

前序:因為迭代使用棧,而出棧順序是先進后出,所以我們在遍歷的時候要改變遍歷的順序,先遍歷右節點,在遍歷左節點,這時候就是左節點先出,符合根左右的習慣

class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();if(root == null){return list;}Stack<TreeNode> s = new Stack<>();s.push(root);while(!s.isEmpty()){TreeNode node = s.pop();list.add(node.val);if(node.right != null){s.push(node.right);}if(node.left != null){s.push(node.left);}}return list;
}}

中序:在使用迭代法寫中序遍歷,就需要借用指針的遍歷來幫助訪問節點,棧則用來處理節點上的元素。

// 中序遍歷順序: 左-中-右 入棧順序: 左-右
class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();if (root == null){return result;}Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;while (cur != null || !stack.isEmpty()){if (cur != null){stack.push(cur);cur = cur.left;}else{cur = stack.pop();result.add(cur.val);cur = cur.right;}}return result;}
}

后序:// 后序遍歷順序 左-右-中 入棧順序:中-左-右 出棧順序:中-右-左, 最后翻轉結果

class Solution {public List<Integer> postorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();if (root == null){return result;}Stack<TreeNode> stack = new Stack<>();stack.push(root);while (!stack.isEmpty()){TreeNode node = stack.pop();result.add(node.val);if (node.left != null){stack.push(node.left);}if (node.right != null){stack.push(node.right);}}Collections.reverse(result);return result;}
}

!!層次遍歷

思路:需要借助輔助隊列來完成統計,即一層一層的入隊。

1.首先聲明一個外部的輔助隊列,用來統計所有的入隊出隊的值,

2.聲明處理一層一層隊列的方法,在方法內部創建一個內部隊列用來處理每一層中出對入對的值

3.每一層結束的標記是什么,就是內部隊列長度為0時,就結束.

public class Solution {public List<List<Integer>> levelOrder(TreeNode root) {List<List<Integer>> resList = new LinkedList<>();if (root == null) {return resList;}Queue<TreeNode> q = new LinkedList<>();q.add(root);while (!q.isEmpty()) {List<Integer> l = new ArrayList<>();int len = q.size();while (len > 0) {TreeNode node = q.poll();l.add(node.val);if (node.left != null)  q.add(node.left);if (node.right != null) q.add(node.right);len--;}resList.add(l);}return resList;
}}

二叉樹自底而上的層次遍歷

給你二叉樹的根節點?root?,返回其節點值?自底向上的層序遍歷?。 (即按從葉子節點所在層到根節點所在的層,逐層從左向右遍歷)

利用LinkedList的addFirst()方法即可,每一次遍歷完都將內部隊列得到的值放在前面,即可倒序

!!注意如果將res實際實現為List類型,就無法使用addFirst()方法?

LinkedList<List<Integer>> res = new LinkedList<>();

public List<List<Integer>> levelOrder(TreeNode root) {LinkedList<List<Integer>> res = new LinkedList<>();if (root == null) {return null;}Queue<TreeNode> q = new LinkedList<>();q.add(root);while (!q.isEmpty()) {List<Integer> l = new LinkedList<>();int len = q.size();while (len-- > 0) {TreeNode node = q.poll();l.add(node.val);if (node.left != null)  q.add(node.left);if (node.right != null) q.add(node.right);}res.addFirst(l);}return res;}

二叉樹的右視圖

給定一個二叉樹的?根節點?root,想象自己站在它的右側,按照從頂部到底部的順序,返回從右側所能看到的節點值。

思路很簡單,即每層層序遍歷時都將最右邊的節點值加入到LIst中,如何判斷為最右邊的節點?

即每一層處理時,隊列長度僅為1時就是最右邊的節點

class Solution {public List<Integer> rightSideView(TreeNode root) {List<Integer> res = new ArrayList<>();if (root == null) {return res;}Queue<TreeNode> q = new LinkedList<>();q.add(root);while (!q.isEmpty()) {int len = q.size();while (len > 0) {TreeNode node = q.poll();if(len == 1){res.add(node.val);}if (node.left != null)  q.add(node.left);if (node.right != null) q.add(node.right);len--;}}return res;}
}

完全二叉樹的節點個數

很簡單, 利用層次遍歷計算每一次處理隊列中結點的數量即可

class Solution {public int countNodes(TreeNode root) {Queue<TreeNode> q = new LinkedList<>();if(root == null){return 0;}q.add(root);int count=0;while(!q.isEmpty()){int size = q.size();for(int i=0;i<size;i++){TreeNode node = q.poll();count++;if(node.left !=null)q.add(node.left);if(node.right !=null)q.add(node.right);}}return count;}
}
  • 二叉樹節點的深度:指從根節點到該節點的最長簡單路徑邊的條數。
  • 二叉樹節點的高度:指從該節點到葉子節點的最長簡單路徑邊的條數。

二叉樹的最大深度

使用層次遍歷到最底一層的節點,既可以判斷深度大小

class Solution {/*** 迭代法,使用層序遍歷*/public int maxDepth(Node root) {if (root == null)   return 0;int depth = 0;Queue<Node> que = new LinkedList<>();que.offer(root);while (!que.isEmpty()){depth ++;int len = que.size();while (len > 0){Node node = que.poll();for (int i = 0; i < node.children.size(); i++)if (node.children.get(i) != null) que.offer(node.children.get(i));len--;}}return depth;}
}
遞歸用法
class Solution {public int maxDepth(TreeNode root) {//終止條件:當樹為空時結束遞歸,并返回當前深度0if(root == null){return 0;}//root的左、右子樹的最大深度int leftDepth = maxDepth(root.left);int rightDepth = maxDepth(root.right);//返回的是左右子樹的最大深度+1return Math.max(leftDepth, rightDepth) + 1;}
}

通過二叉樹的最大深度遞歸用法,我們可以總結一下里面遞歸的過程,比如有一個{1,2,3,4,5,6,7,8}的完全二叉樹

1.首先,節點1不為空,進入棧內,然后先調用左子樹,這時,節點2入棧,4入棧,8入棧,因為每次都先判斷的左子樹。

2.這時候棧內又遞歸調用的maxdepth(8),maxdepth(4),maxdepth(2),maxdepth(1),然后節點8沒有左右子樹,所以leftdepth和rightdepth都是0,然后返回0+1,這是maxdepth(8)出棧了,回到maxdepth(4),這時候傳遞的leftdepth=1,因為8時4的左節點,節點4沒有右節點,所以rightdepth=0,這是返回1+1,4出棧,返回maxdepth(2),leftdepth=2,rightdepth=1,所以返回2+1,2出棧,返回maxdepth(1),leftdepth=3,進入右子樹。。。。

3.最后又返回maxdepth(1),leftdepth=3,rightdepth=2,所以最后的值返回的時3+1

遞歸調用棧過程
  1. 節點1入棧

    • 調用左子樹?maxDepth(2)

  2. 節點2入棧

    • 調用左子樹?maxDepth(4)

  3. 節點4入棧

    • 調用左子樹?maxDepth(8)

  4. 節點8入棧

    • 左子樹為空,返回?0

    • 右子樹為空,返回?0

    • 當前節點8的深度為?max(0, 0) + 1 = 1出棧

  5. 回到節點4

    • leftDepth = 1(來自節點8)。

    • 調用右子樹?maxDepth(null),返回?0

    • 當前節點4的深度為?max(1, 0) + 1 = 2出棧

  6. 回到節點2

    • leftDepth = 2(來自節點4)。

    • 調用右子樹?maxDepth(5)

  7. 節點5入棧

    • 左子樹為空,返回?0

    • 右子樹為空,返回?0

    • 當前節點5的深度為?max(0, 0) + 1 = 1出棧

  8. 回到節點2

    • rightDepth = 1(來自節點5)。

    • 當前節點2的深度為?max(2, 1) + 1 = 3出棧

  9. 回到節點1

    • leftDepth = 3(來自節點2)。

    • 調用右子樹?maxDepth(3)

  10. 節點3入棧

    • 調用左子樹?maxDepth(6)

  11. 節點6入棧

    • 左子樹為空,返回?0

    • 右子樹為空,返回?0

    • 當前節點6的深度為?max(0, 0) + 1 = 1出棧

  12. 回到節點3

    • leftDepth = 1(來自節點6)。

    • 調用右子樹?maxDepth(7)

  13. 節點7入棧

    • 左子樹為空,返回?0

    • 右子樹為空,返回?0

    • 當前節點7的深度為?max(0, 0) + 1 = 1出棧

  14. 回到節點3

    • rightDepth = 1(來自節點7)。

    • 當前節點3的深度為?max(1, 1) + 1 = 2出棧

  15. 回到節點1

    • rightDepth = 2(來自節點3)。

    • 當前節點1的深度為?max(3, 2) + 1 = 4出棧

判斷平衡二叉樹?

平衡二叉樹每個節點的左右兩個子樹的高度差的絕對值不超過1。而本體并不是考察的深度,而是高度,每一顆二叉子樹都要符合高度差的絕對值不超過1

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

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

相關文章

使用ZYNQ芯片和LVGL框架實現用戶高刷新UI設計系列教程(第九講)

這一期講解GUI_guider中的容器控件的使用以及相關函數&#xff0c;容器本質上是具有布局和自動調整大小功能的基本對象 &#xff0c;通常用來裝載其他子控件。 打開上一期的項目&#xff0c;在工具欄中選中容器控件拖拽到界面中&#xff0c;具體如圖所示&#xff1a; 容器默認…

qt QGroupButton 實現兩個QPushButton的互斥

import sys from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QButtonGroup, QVBoxLayoutclass ExampleApp(QWidget):def __init__(self):super().__init__()self.initUI()def initUI(self):# 創建兩個 QPushButtonself.button1 QPushButton("按鈕1&quo…

工業物聯網的可視化編程革新:Node-RED與邊緣計算的深度融合-縱橫智控

在工業物聯網的演進歷程中&#xff0c;可視化編程工具正成為打破技術壁壘的核心力量。Node-RED作為開源的可視化編程平臺&#xff0c;通過其獨特的拖拽式邏輯構建能力&#xff0c;為設備連接、數據處理與業務邏輯設計提供了全新范式。本文將深入解析Node-RED的技術優勢&#xf…

Uniapp:view容器(容器布局)

目錄 一、基本概述二、屬性說明三、常用布局3.1 橫向布局3.2 縱向布局3.3 更多布局3.3.1 縱向布局-自動寬度3.3.2 縱向布局-固定寬度3.3.3 橫向布局-自動寬度3.3.4 橫向布局-居中3.3.5 橫向布局-居右3.3.6 橫向布局-平均分布3.3.7 橫向布局-兩端對齊3.3.8 橫向布局-自動填充3.3…

(最新)華為 2026 屆校招實習-硬件技術工程師-硬件通用/單板開發—機試題—(共14套)(每套四十題)

&#xff08;最新&#xff09;華為 2026 屆校招實習-硬件技術工程師-硬件通用/單板開發—機試題—&#xff08;共14套&#xff09;&#xff08;每套四十題&#xff09; 本套題目為硬件通用題目&#xff0c;適合多個崗位方向&#xff0c;如下 **崗位——硬件技術工程師 崗位意向…

AWS Lambda 架構深入探究

AWS Lambda 是現代云架構中最受歡迎的服務之一,因其能夠在完全托管的無服務器環境中運行代碼而廣受認可。然而,盡管 Lambda 廣受歡迎,許多開發者和架構師對它的底層運作機制卻知之甚少,常常將其視為“編寫能夠在云端神奇運行的代碼”的簡單方法。 本文將探討 AWS Lambda 背…

Android audio系統五 AudioPolicy 策略配置詳解

引用&#xff1a;Android 音頻策略配置文件解析流程 audio_policy_configuration.xml 是 Android 音頻系統的核心配置文件&#xff0c;它定義了音頻硬件接口、設備路由和基本策略。下面我將詳細介紹這個文件的結構、關鍵配置項和實際應用。audio_policy_configuration.xml 是 …

4.21日學習--引用

引用本質&#xff1a;引用的本質在 c 內部實現是一個指針常量。 代碼中 int& ref a; 可以理解為 int* const ref &a;&#xff08;指針常量&#xff09;。 指針常量&#xff1a;指針指向不可變&#xff08;綁定 a 后&#xff0c;不能再指向其他變量&#xff09;&…

2.1 數據處理

1. 數據獲取方法 掌握公開數據集的使用、數據質量評估指標、了解常見的網絡爬蟲技術 &#x1f9e9; 一、公開數據集的使用 ? 常見平臺&#xff08;一定要熟&#xff09; 平臺簡介示例數據集Hugging Face Datasets專注 NLP、CV 領域的大模型訓練數據集庫IMDB、SQuAD、Common …

Qt QWidget和QML實現窗口拖動源碼分享

一、QWidget實現窗口拖動 .hpp QPoint pressedPoint; bool leftBtnPressed false;.cpp bool PetWidget::eventFilter(QObject *obj, QEvent *event) {if(obj this){if(event->type() QEvent::MouseButtonPress){QMouseEvent* e static_cast<QMouseEvent *>(event)…

在pycharm中搭建yolo11分類檢測系統--PyQt5學習(二)

第二部分 測試本地pycharm通過程序連接遠程服務器autodl 模型的推理需要借助遠程服務器autodl&#xff0c;但是界面的運行是在pycharm中&#xff0c;我的設想是按鈕調用一個py文件就好了。 1. 本地運行PyQt5界面。 2. 當需要載入權重時&#xff0c;通過SSH連接到AutodL服務…

前端框架的“快閃“時代:我們該如何應對技術迭代的洪流?

引言&#xff1a;前端開發者的"框架疲勞" “上周剛學完Vue 3的組合式API&#xff0c;這周SolidJS又火了&#xff1f;”——這恐怕是許多前端開發者2023年的真實心聲。前端框架的迭代速度已經達到了令人目眩的程度&#xff0c;GitHub每日都有新框架誕生&#xff0c;n…

基于YOLO11的遛狗牽繩識別預警系統

基于YOLO11的遛狗牽繩識別預警系統 【包含內容】 【一】項目提供完整源代碼及詳細注釋 【二】系統設計思路與實現說明 【三】預訓練模型與數據集說明 【四】需要列出所有的類別&#xff0c;并且加入識別的類別數量&#xff1a;4類 0: dog (狗) 1: leash (牽引繩) 2: person …

Spring MVC 一個簡單的多文件上傳

原始代碼逐行解釋 PostMapping("/uploads") // ① 聲明處理POST請求&#xff0c;路徑為"/uploads" ResponseBody // ② 直接返回數據到響應體&#xff0c;不進行視圖解析 public String uploads(MultipartFile[] files, // …

C++繼承(最詳細)

目錄 1.繼承的概念以及定義 1.1 繼承的概念 1.2 繼承的定義 ?編輯 2.繼承中的作用域 3.基類和派生類間的轉換 4.派生類的默認成員函數 5.實現不被繼承的類 6.継承與友元 ?編輯 7.繼承與靜態成員 8.多繼承及其菱形繼承問題 8.2 虛繼承 8.3 來看一個小題 9.繼承…

day35圖像處理OpenCV

文章目錄 一、圖像預處理17 直方圖均衡化17.1繪制直方圖17.2直方圖均衡化1. 自適應直方圖均衡化2. 對比度受限的自適應直方圖均衡化3. 示例 19 模板匹配 一、圖像預處理 17 直方圖均衡化 直方圖&#xff1a;反映圖像像素分布的統計圖&#xff0c;橫坐標就是圖像像素的取值&…

【音視頻】FFmpeg內存模型

FFmpeg內存模型 從現有的Packet拷貝一個新Packet的時候&#xff0c;有兩種情況&#xff1a; 兩個Packet的buf引用的是同一數據緩存空間&#xff0c;這時候要注意數據緩存空間的釋放問題&#xff1b;兩個Packet的buf引用不同的數據緩存空間&#xff0c;每個Packet都有數據緩存…

1.2軟考系統架構設計師:系統架構的定義與作用 - 練習題附答案及超詳細解析

系統架構定義與作用綜合知識單選題 題目覆蓋核心概念、發展歷程、設計原則、評估標準及易混淆點&#xff0c;附答案解析&#xff1a; 1. 系統架構的標準定義源自于以下哪個標準&#xff1f; A. ISO/IEC 9126 B. IEEE 1471-2000 C. TOGAF 9.2 D. ITIL v4 答案&#xff1a;B 簡…

go語言對http協議的支持

http&#xff1a;無狀態協議&#xff0c;是互聯網中使用http使用http實現計算機和計算機之間的請求和響應 使用純文本方式發送和接受協議數據&#xff0c;不需要借助專門工具進行分析就知道協議中的數據 服務器端的幾個概念 Request&#xff1a;用戶請求的信息&#xff0c;用…

iscsi服務端安裝及配置

1. 安裝targetcli軟件包 yum install -y targetcli 2. 啟動target服務 systemctl start target systemctl enable target 3. 配置防火墻 firewall-cmd --add-port"3260/tcp" 3. 準備一個物理分區&#xff08;或者邏輯分區&#xff09;…