算法----二叉搜索樹(BST)

系列文章目錄

算法----滑動窗口
算法----二叉樹


文章目錄

  • 系列文章目錄
  • 二叉搜索樹心法(特性篇)
  • 二叉搜索樹心法(基操篇)
    • 1、判斷 BST 的合法性
    • 2、在 BST 中搜索元素
    • 3、在 BST 中插入一個數
    • 4、在 BST 中刪除一個數


二叉搜索樹心法(特性篇)

BST 的特性:

  1. 對于 BST 的每一個節點 node,左子樹節點的值都比 node 的值要小,右子樹節點的值都比 node 的值大。

  2. 對于 BST 的每一個節點 node,它的左側子樹和右側子樹都是 BST。

從做算法題的角度來看 BST,除了它的定義,還有一個重要的性質:BST 的中序遍歷結果是有序的(升序)

void traverse(TreeNode* root) {if (root == nullptr)  return;traverse(root->left);// 中序遍歷(升序)print(root->val);traverse(root->right);
}

BST 的中序遍歷代碼可以升序打印節點的值,那如果我想降序打印節點的值怎么辦?很簡單,只要把遞歸順序改一下,先遍歷右子樹,后遍歷左子樹,就能夠降序遍歷節點

void traverse(TreeNode* root) {if (root == nullptr)  return;traverse(root->right);// 中序遍歷(降序)print(root->val);traverse(root->left);
}

二叉搜索樹心法(基操篇)

1、判斷 BST 的合法性

按照 BST 左小右大的特性,每個節點想要判斷自己是否是合法的 BST 節點,要做的事好像是比較自己和左右孩子,比左孩子大,右孩子小。就會寫出這樣的代碼:

bool isValidBST(TreeNode* root) {if (root == nullptr) return true;// root 的左邊應該更小if (root->left != nullptr && root->left->val >= root->val)return false;// root 的右邊應該更大if (root->right != nullptr && root->right->val <= root->val)return false;return isValidBST(root->left)&& isValidBST(root->right);
}

這樣寫出的代碼是錯誤的,BST 的每個節點還要應該要小于右邊子樹的所有節點,下面這個二叉樹顯然不是 BST,因為節點 7 的左子樹中有一個節點 8,但是上面的算法代碼會把它判定為合法 BST:
在這里插入圖片描述
錯誤的原因在于,對于每一個節點 root,代碼值檢查了它的左右孩子節點是否符合左小右大的原則;但是根據 BST 的定義,root 的整個左子樹都要小于 root.val,整個右子樹都要大于 root.val

問題是,對于某一個節點 root,他只能管得了自己的左右子節點,怎么把 root 的約束傳遞給左右子樹呢?請看正確的代碼:

class Solution {
public:bool isValidBST(TreeNode* root) {return _isValidBST(root, nullptr, nullptr);}// 定義:該函數返回 root 為根的子樹的所有節點是否滿足 max->val > root->val > min->valbool _isValidBST(TreeNode* root, TreeNode* min, TreeNode* max) {// base caseif (root == nullptr) return true;// 若 root->val 不符合 max 和 min 的限制,說明不是合法 BSTif (min != nullptr && root->val <= min->val) return false;if (max != nullptr && root->val >= max->val) return false;// 根據定義,限定左子樹的最大值是 root->val,右子樹的最小值是 root->valreturn _isValidBST(root->left, min, root) && _isValidBST(root->right, root, max);}
};

2、在 BST 中搜索元素

// 定義:在以 root 為根的 BST 中搜索值為 target 的節點,返回該節點
TreeNode* searchBST(TreeNode* root, int target) {if (root == nullptr) {return nullptr;}// 去左子樹搜索if (root->val > target) {return searchBST(root->left, target);}// 去右子樹搜索if (root->val < target) {return searchBST(root->right, target);}// 當前節點就是目標值return root;
}

3、在 BST 中插入一個數

// 定義:在以 root 為根的 BST 中插入 val 節點,返回插入后的根節點
class Solution {
public:TreeNode* insertIntoBST(TreeNode* root, int val) {if (root == nullptr) {// 找到空位置插入新節點return new TreeNode(val);}// 去右子樹找插入位置if (root->val < val) {root->right = insertIntoBST(root->right, val);}// 去左子樹找插入位置if (root->val > val) {root->left = insertIntoBST(root->left, val);}// 返回 root,上層遞歸會接收返回值作為子節點return root;}
};

4、在 BST 中刪除一個數

這個問題稍微復雜,跟插入操作類似,先「找」再「改」,先把框架寫出來再說:

TreeNode* deleteNode(TreeNode* root, int key) {if (root->val == key) {// 找到啦,進行刪除} else if (root->val > key) {// 去左子樹找root->left = deleteNode(root->left, key);} else if (root->val < key) {// 去右子樹找root->right = deleteNode(root->right, key);}return root;
}

找到目標節點了,比方說是節點 A,如何刪除這個節點,這是難點。因為刪除節點的同時不能破壞 BST 的性質。有三種情況,用圖片來說明。

  • 情況 1:A 恰好是末端節點,兩個子節點都為空,那么它可以直接被刪除。
    在這里插入圖片描述

    if (root.left == null && root.right == null)return null;
    
  • 情況 2:A 只有一個非空子節點,那么它要讓這個孩子接替自己的位置。
    在這里插入圖片描述

    // 排除了情況 1 之后
    if (root.left == null) return root.right;
    if (root.right == null) return root.left;
    
  • A 有兩個子節點,麻煩了,為了不破壞 BST 的性質,A 必須找到 左子樹中最大的那個節點,或者右子樹中最小的那個節點 來接替自己。我們以第二種方式講解。
    在這里插入圖片描述

    if (root.left != null && root.right != null) {// 找到右子樹的最小節點TreeNode minNode = getMin(root.right);// 把 root 改成 minNoderoot.val = minNode.val;// 轉而去刪除 minNoderoot.right = deleteNode(root.right, minNode.val);
    }
    

三種情況分析完畢,填入框架,最終的代碼:

class Solution {
public:// 定義:在以 root 為根的 BST 中刪除值為 key 的節點,返回完成刪除后的根節點TreeNode* deleteNode(TreeNode* root, int key) {if (root == nullptr) return nullptr;if (root->val == key) {// 這兩個 if 把情況 1 和 2 都正確處理了if (root->left == nullptr) return root->right;if (root->right == nullptr) return root->left;// 處理情況 3// 獲得右子樹最小的節點TreeNode* minNode = getMin(root->right);// 刪除右子樹最小的節點root->right = deleteNode(root->right, minNode->val);// 用右子樹最小的節點替換 root 節點minNode->left = root->left;minNode->right = root->right;root = minNode;} else if (root->val > key) {root->left = deleteNode(root->left, key);} else if (root->val < key) {root->right = deleteNode(root->right, key);}return root;}TreeNode* getMin(TreeNode* node) {// BST 最左邊的就是最小的while (node->left != nullptr) node = node->left;return node;}
};

上述代碼在處理情況 3 時通過一系列略微復雜的鏈表操作交換 root 和 minNode 兩個節點:

// 獲得右子樹最小的節點
TreeNode* minNode = getMin(root->right);
// 刪除右子樹最小的節點
root->right = deleteNode(root->right, minNode->val);	//妙筆:用原函數遞歸刪除
// 用右子樹最小的節點替換 root 節點
minNode->left = root->left;
minNode->right = root->right;
root = minNode;

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

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

相關文章

GitHub Actions打包容器,推送 AWS ECR 并使 EKS 自動拉取以完成發版部署

以下是關于 EKS 直接拉取 ECR 鏡像的解答&#xff0c;以及如何通過 GitHub Actions 將項目打包為容器、推送至 AWS ECR 并使 EKS 自動拉取以完成發版部署的詳細步驟。當前時間為 2025 年 7 月 23 日下午 12:27 HKT&#xff0c;基于最新技術實踐提供方案。1. EKS 直接拉取 ECR 鏡…

洛谷刷題7.24

P1087 [NOIP 2004 普及組] FBI 樹 - 洛谷 簡單的二叉樹遍歷 #include<bits/stdc.h> #define ll long long using namespace std; int n; char show(string s){if(s.find(1)string::npos) return B;if(s.find(0)string::npos) return I;return F; } void dfs(string s){…

FreeRTOS—二值信號量

文章目錄一、二值信號量簡介二、二值信號量相關的API函數2.1.動態方式創建二值信號量2.2.獲取信號量2.3.釋放信號量三、實驗3.1.實驗設計3.2.軟件設計一、二值信號量簡介 二值信號量的本質是一個隊列長度為 1 的隊列&#xff0c;該隊列就只有空和滿兩種情況&#xff0c;也就是…

挖掘錄屏寶藏:Screenity 深度解析與使用指南

挖掘錄屏寶藏&#xff1a;Screenity 深度解析與使用指南 在數字內容創作與信息分享日益頻繁的今天&#xff0c;錄屏軟件成為了眾多創作者、教育者和辦公族的必備工具。今天&#xff0c;我要給大家介紹一款在 GitHub 上收獲了大量關注的開源錄屏軟件 ——Screenity。它功能強大…

4.1.2 XmlInclude 在 C# 中的作用及示例

xmlInclude 是 .NET 中用于 XML 序列化的一個重要特性,XmlInclude 的主要作用是: 1.告知 XML 序列化器可能遇到的派生類型 2.解決多態類型的序列化和反序列化問題 3.允許基類序列化時包含派生類信息 當你有基類引用指向派生類對象時,如果不使用 XmlInclude,序列化器…

ARM匯編常見偽指令及其用法示例

偽指令不是指令&#xff0c;偽指令和指令的根本區別是經過編譯后會不會生成機器碼。 偽指令的意義在于指導編譯過程。 偽指令是和具體的編譯器相關的&#xff0c;我們使用gnu工具鏈&#xff0c;因此學習gnu環境下的匯編偽指令。在 ARM 匯編中&#xff0c;偽指令&#xff08;Pse…

算法調試技巧

引言算法調試常比編寫更耗時&#xff0c;尤其是動態規劃、遞歸等邏輯復雜的代碼。本文分享一套系統化的調試方法&#xff0c;幫助快速定位問題。一、調試前的準備代碼格式化使用統一縮進&#xff08;4 空格&#xff09;和命名規范&#xff0c;避免因格式混亂導致的邏輯誤讀。邊…

每日功能分享|讓觀看者體驗“無縫鏈接”觀看的功能——視頻自動續播功能

你是否遇到過這樣的困擾——看到一半的視頻&#xff0c;關閉后卻忘記進度&#xff0c;再打開時需要手動拖拽尋找上次的觀看位置&#xff1f;如今&#xff0c;“視頻自動續播功能”完美解決了這一痛點&#xff01;無論是在線教育課程、影視劇集還是企業內部員工培訓&#xff0c;…

AWS: 云上偵探手冊,七步排查ALB與EC2連接疑云

今天&#xff0c;咱們來聊一個對于許多剛接觸AWS的運維同學來說&#xff0c;既常見又有點頭疼的話題&#xff1a;如何優雅地排查和解決AWS上ALB&#xff08;Application Load Balancer&#xff09;暴露EC2服務時遇到的種種疑難雜癥。 最近&#xff0c;我剛幫一個朋友解決了類似…

EIDE 創建基于STM32-HD的項目快速創建流程

EIDE 創建基于STM32-HD的項目流程芯片系列定義宏Flash 大小RAM 大小STM32F10x_HD#define STM32F10X_HD256KB~512KB48KB~64KBSTM32F10x_MD#define STM32F10X_MD64KB~128KB20KBSTM32F10x_LD#define STM32F10X_LD16KB~32KB4KB~10KB 新建項目遠程倉庫獲取裸機開發程序STM(意法半導體…

使用 QLExpress 構建靈活可擴展的業務規則引擎

目錄 一、什么是 QLExpress&#xff1f; 二、推薦系統中的規則腳本應用 1 場景描述 2 推薦規則腳本&#xff08;QLExpress&#xff09; 3 系統實現 4 執行結果 5 推薦系統應用建議 三、風控系統中的規則判定 1 場景描述 2 風控規則腳本&#xff08;QLExpress&#xff…

【硬件-筆試面試題】硬件/電子工程師,筆試面試題-13,(知識點:DC-DC電源,相位裕度,增益裕度)

目錄 1、題目 2、解答 相位裕度 增益裕度 3、相關知識點 一、波特圖 二、相位裕度 三、增益裕度 四、在 DC - DC 電源中的應用 【硬件-筆試面試題】硬件/電子工程師&#xff0c;筆試面試題匯總版&#xff0c;持續更新學習&#xff0c;加油&#xff01;&#xff01;&a…

學生信息管理系統 - HTML實現增刪改查

學生信息管理系統 - HTML實現增刪改查 效果圖 代碼 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><titl…

Agile簡介

Agile&#xff08;敏捷&#xff09;是一種軟件開發方法論&#xff0c;核心是通過快速迭代、靈活響應變化&#xff0c;解決傳統軟件開發中周期長、需求變更困難等問題&#xff0c;最終高效交付符合用戶實際需求的產品。 一、Agile 的起源&#xff1a;為什么需要敏捷&#xff1f;…

關于 URL 中 “+“ 號變成空格的問題

當你在 URL 中傳遞參數時&#xff0c;加號 () 會被自動轉換為空格&#xff0c;這是 URL 編碼的標準行為。問題原因在 URL 中&#xff1a;空格會被編碼為 號當 URL 被解碼時&#xff0c; 號又會被轉換回空格這會導致原始數據中的 號丟失解決方案你需要對參數值進行正確的 URL …

綜合實驗(2)

文章目錄 目錄 文章目錄 前言 OSPF運行在GRE隧道概述 典型應用場景 OSPF over GRE 配置 總結 前言 OSPF運行在GRE隧道概述 GRE&#xff08;Generic Routing Encapsulation&#xff09;隧道是一種通過封裝原始數據包在IP網絡中創建虛擬點對點連接的隧道技術。OSPF&#xff08;…

【應急響應工具教程】司稽(Whoamifuck):純Shell打造的Linux應急響應利器

1、工具簡介司稽&#xff08;Whoamifuck或Chief-Inspector,簡稱"who"&#xff09;&#xff0c;永恒之鋒發布的第一款開源工具&#xff0c;這是一款由shell編寫的Linux應急響應腳本&#xff0c;能對基本的檢查項進行輸出和分析&#xff0c;并支持一些擴展的特色功能。…

新手操作steam搬磚項目,應該如何快速起步

大家好哦&#xff0c;我是阿陽&#xff0c;今天繼續給大家分享一些steam搬磚的知識。在我們操作過程中&#xff0c;問題問得最多的就是&#xff0c;新手應該怎么做&#xff1f;首先&#xff0c;那我們得先來了解-下,什么是steam搬磚,它的項目原理是什么&#xff0c;其次針對于這…

rt-thread加一個庫

背景 官方軟件包里沒有的 可以以庫或組件形式加入 本次僅為了驗證&#xff0c;加到庫 過程 下載源碼 假設為 lib_demo 自己的板子目錄為bsp/stm32 代碼目錄結構 bsp/stm32librarieslib_demo //新建文件夾src //把lib_demo里源碼文件放進來inc //把lib_demo里頭文件放進來SConsc…

c++深拷貝和淺拷貝

一、淺拷貝本質&#xff1a;簡單地復制對象的成員值。如果成員里有指針&#xff0c;新對象和原對象的指針會指向同一塊內存。比如你有對象 A&#xff0c;里面指針 p 指向堆內存 0x123&#xff1b;用 A 拷貝出對象 B&#xff0c;B 的指針 p 也指向 0x123。問題&#xff1a;若其中…