算法學習筆記:26.二叉搜索樹(生日限定版)——從原理到實戰,涵蓋 LeetCode 與考研 408 例題

二叉搜索樹(Binary Search Tree,簡稱 BST)是一種特殊的二叉樹,因其高效的查找、插入和刪除操作,成為計算機科學中最重要的數據結構之一。BST 的核心特性是 “左小右大”,這一特性使其在數據檢索、排序和索引等場景中發揮著關鍵作用。


二叉搜索樹的定義與核心性質

定義

二叉搜索樹是一種二叉樹,其中每個節點的左子樹中所有節點的值小于該節點的值,右子樹中所有節點的值大于該節點的值。這一性質被稱為 “左小右大” 原則,且該原則對所有子樹都成立。

形式化定義

對于 BST 中的任意節點node,滿足:

  • 左子樹中所有節點的值 < node.val
  • 右子樹中所有節點的值 > node.val
  • 左子樹和右子樹本身也是二叉搜索樹

核心性質

  1. 中序遍歷特性:BST 的中序遍歷(左→根→右)結果是嚴格升序的。這是 BST 最核心的性質,也是解決多數 BST 問題的關鍵。
  2. 查找效率:在平衡的 BST 中,查找、插入、刪除操作的時間復雜度為O(logn);在最壞情況下(如退化為鏈表),時間復雜度為O(n)。
  3. 唯一性:給定一組數據,可能存在多個 BST 結構,但中序遍歷結果唯一(即數據的升序序列)。

結構圖示

二叉搜索樹的基本操作

查找操作

思路

利用BST“左小右大”的性質,從根節點開始:

- 若目標值等于當前節點值,返回該節點。

- 若目標值小于當前節點值,遞歸查找左子樹。

- 若目標值大于當前節點值,遞歸查找右子樹。

- 若遍歷到空節點,返回`null`(未找到)。

實現代碼
class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int x) {val = x;}}public TreeNode searchBST(TreeNode root, int val) {if (root == null || root.val == val) {return root;}return val < root.val ? searchBST(root.left, val) : searchBST(root.right, val);}

插入操作

思路:插入過程類似查找,找到合適的空位置插入新節點:

  • 若當前節點為空,創建新節點返回。
  • 若插入值小于當前節點值,遞歸插入左子樹。
  • 若插入值大于當前節點值,遞歸插入右子樹。
  • (BST 通常不允許重復值,若需處理重復值,可約定插入右子樹)
插入過程圖示

實現代碼
public TreeNode insertIntoBST(TreeNode root, int val) {if (root == null) {return new TreeNode(val); // 找到插入位置}if (val < root.val) {root.left = insertIntoBST(root.left, val); // 插入左子樹} else {root.right = insertIntoBST(root.right, val); // 插入右子樹}return root;}

刪除操作

刪除操作是 BST 中最復雜的操作,需根據節點的子樹情況分三種處理:

  1. 葉子節點(無左右子樹):直接刪除,返回null。
  2. 單子樹節點(只有左或右子樹):刪除節點,用子樹替代其位置。
  3. 雙子樹節點(有左右子樹)
    • 找到該節點的前驅(左子樹中最大節點)或后繼(右子樹中最小節點)。
    • 用前驅(或后繼)的值替換當前節點的值。
    • 遞歸刪除前驅(或后繼)節點。
刪除過程圖示

(刪除節點 6)

實現代碼
public TreeNode deleteNode(TreeNode root, int key) {if (root == null) {return null; // 未找到待刪節點}if (key < root.val) {root.left = deleteNode(root.left, key); // 遞歸刪除左子樹} else if (key > root.val) {root.right = deleteNode(root.right, key); // 遞歸刪除右子樹} else {// 找到待刪節點,分三種情況if (root.left == null) {return root.right; // 無左子樹,用右子樹替代} else if (root.right == null) {return root.left; // 無右子樹,用左子樹替代} else {// 有左右子樹,找左子樹最大節點(前驅)TreeNode predecessor = findMax(root.left);root.val = predecessor.val; // 替換值root.left = deleteNode(root.left, predecessor.val); // 刪除前驅}}return root;}// 查找左子樹最大節點(最右節點)private TreeNode findMax(TreeNode node) {while (node.right != null) {node = node.right;}return node;}

LeetCode 例題實戰

例題 1:98. 驗證二叉搜索樹(中等)

題目描述:給你一個二叉樹的根節點 root ,判斷其是否是一個有效的二叉搜索樹。有效 BST 定義為:

  • 節點的左子樹只包含小于當前節點的數。
  • 節點的右子樹只包含大于當前節點的數。
  • 所有左子樹和右子樹自身必須也是二叉搜索樹。

示例

輸入:root = [2,1,3]

輸出:true(1<2<3,符合BST)

輸入:root = [5,1,4,null,null,3,6]

輸出:false(4的左子樹有3<4,但4<5不成立)

解題思路

利用 BST 中序遍歷為升序的特性,或遞歸檢查每個節點的左右邊界:

  1. 中序遍歷法:中序遍歷 BST,若結果非嚴格升序,則不是有效 BST。
  2. 遞歸邊界法:為每個節點設置上下邊界(low和high):
    • 根節點的邊界為(-∞, +∞)。
    • 左子樹節點的邊界為(low, root.val)。
    • 右子樹節點的邊界為(root.val, high)。
    • 若節點值超出邊界,則無效。
方法 2(遞歸邊界法)Java 代碼
class Solution {public boolean isValidBST(TreeNode root) {return isValid(root, Long.MIN_VALUE, Long.MAX_VALUE);}private boolean isValid(TreeNode node, long low, long high) {if (node == null) {return true; // 空樹是有效BST}// 節點值必須在(low, high)范圍內if (node.val <= low || node.val >= high) {return false;}// 左子樹邊界:(low, node.val),右子樹邊界:(node.val, high)return isValid(node.left, low, node.val) && isValid(node.right, node.val, high);}}
復雜度分析
  • 時間復雜度:O (n),遍歷所有節點一次。
  • 空間復雜度:O (n),遞歸棧深度最壞為 n(退化為鏈表)。

例題 2:108. 將有序數組轉換為二叉搜索樹(簡單)

題目描述:給你一個整數數組 nums ,其中元素已經按升序排列,請你將其轉換為一棵高度平衡的二叉搜索樹。高度平衡的二叉樹是指每個節點的左右兩個子樹的高度差的絕對值不超過 1。

示例

輸入:nums = [-10,-3,0,5,9]

輸出:[0,-3,9,-10,null,5](或其他平衡BST結構)

解題思路

利用 BST 中序遍歷為升序的逆過程:

  1. 選中間元素為根:平衡 BST 的根應是數組中間元素,確保左右子樹大小均衡。
  2. 遞歸構建
    • 左子樹由數組左半部分構建。
    • 右子樹由數組右半部分構建。
    • 遞歸終止條件:數組為空時返回null。
構建過程圖示

代碼實現
class Solution {public TreeNode sortedArrayToBST(int[] nums) {return build(nums, 0, nums.length - 1);}private TreeNode build(int[] nums, int left, int right) {if (left > right) {return null; // 遞歸終止}// 選擇中間元素作為根(平衡關鍵)int mid = left + (right - left) / 2;TreeNode root = new TreeNode(nums[mid]);// 遞歸構建左右子樹root.left = build(nums, left, mid - 1);root.right = build(nums, mid + 1, right);return root;}}
復雜度分析
  • 時間復雜度:O (n),每個元素構建一個節點。
  • 空間復雜度:O (logn),遞歸棧深度為平衡樹的高度 logn。

考研 408 例題解析

例題 1:概念辨析題(選擇題)

題目:下列關于二叉搜索樹的敘述中,正確的是( )。

A. 二叉搜索樹的中序遍歷序列一定是遞增的

B. 二叉搜索樹中任意節點的左子樹高度一定小于右子樹高度

C. 對二叉搜索樹進行前序遍歷,再根據前序遍歷序列可以唯一重構該 BST

D. 在二叉搜索樹中查找某元素的時間復雜度一定是 O (logn)

答案:A

解析

  • A 正確:BST 的核心性質就是中序遍歷為嚴格遞增序列。
  • B 錯誤:BST 不要求左右子樹高度平衡(平衡 BST 如 AVL 樹才要求)。
  • C 錯誤:前序遍歷序列無法唯一重構 BST(如前序 [2,1,3] 和 [2,3,1] 可能對應不同 BST)。
  • D 錯誤:在最壞情況下(退化為鏈表),查找時間復雜度為 O (n)。

例題 2:算法設計題(408 高頻考點)

題目:設計一個算法,在二叉搜索樹中找出第 k 小的元素,并分析算法的時間復雜度。

解題思路

利用 BST 中序遍歷為升序的特性,中序遍歷的第 k 個元素即為第 k 小元素:

  1. 遞歸中序遍歷:記錄遍歷順序,當計數達到 k 時返回當前節點值。
  2. 迭代中序遍歷:用棧模擬中序遍歷,彈出第 k 個節點時返回其值。
方法 2(迭代法)實現代碼
public int kthSmallest(TreeNode root, int k) {Deque<TreeNode> stack = new ArrayDeque<>();TreeNode curr = root;int count = 0;while (curr != null || !stack.isEmpty()) {// 左子樹入棧while (curr != null) {stack.push(curr);curr = curr.left;}// 彈出棧頂(中序遍歷的當前節點)curr = stack.pop();count++;if (count == k) {return curr.val; // 找到第k小元素}// 處理右子樹curr = curr.right;}return -1; // 無效輸入(k超出范圍)}
復雜度分析
  • 時間復雜度:O (h + k),h 為 BST 高度,最壞為 O (n + k)(鏈表),平均為 O (logn + k)。
  • 空間復雜度:O (h),棧存儲的節點數為樹的高度。

二叉搜索樹的擴展與應用

實際應用場景

  • 數據庫索引:MySQL 的 B + 樹索引基于 BST 擴展,支持高效范圍查詢。
  • 有序映射 / 集合:Java 中的TreeMap和TreeSet底層為紅黑樹(一種平衡 BST)。
  • 排序與檢索:利用 BST 的插入和中序遍歷實現排序,時間復雜度 O (nlogn)。

與其他樹結構的關系

樹結構

特點

適用場景

二叉搜索樹(BST)

左小右大,中序升序

基礎檢索、排序

AVL 樹

平衡 BST,左右高差≤1

需嚴格平衡的場景

紅黑樹

近似平衡 BST,黑高一致

插入刪除頻繁的場景(如集合)

B + 樹

多路 BST,葉子節點成鏈表

數據庫索引

考研 408 備考要點

  • 核心考點:BST 的定義與性質、中序遍歷特性、插入 / 刪除 / 查找操作。
  • 重點掌握
  1. 利用中序遍歷解決 BST 相關問題(如驗證 BST、找第 k 小元素)。
  2. 插入和刪除操作的遞歸實現,尤其是刪除時的節點替換邏輯。
  3. BST 與平衡樹的區別,以及時間復雜度分析。
  • 常見錯誤
    • 忽略 BST 中 “嚴格大于 / 小于” 的約束(允許等于時需明確約定)。
    • 刪除節點時未正確處理雙子樹情況,導致 BST 性質被破壞。

總結

二叉搜索樹作為一種基礎且重要的數據結構,其 “左小右大” 的特性和中序遍歷升序的性質,使其在數據檢索和排序中有著廣泛應用。本文通過 LeetCode 例題(驗證 BST、有序數組轉 BST)展示了 BST 的核心應用,通過考研 408 例題解析了概念辨析和算法設計思路,結合 SVG 圖示直觀呈現了 BST 的結構及操作過程。

掌握 BST 的關鍵在于:

  1. 深刻理解中序遍歷為升序這一核心性質,并用其解決各類衍生問題。
  2. 熟練實現插入、刪除、查找等基本操作,尤其是刪除操作的三種情況處理。
  3. 明確 BST 與平衡樹的區別,理解其時間復雜度的最壞與平均情況。

在考研備考中,BST 是數據結構部分的重點,需結合實例深入理解其操作原理和性質應用,為學習更復雜的樹結構(如紅黑樹、B + 樹)奠定基礎。

希望本文能夠幫助讀者更深入地理解二叉搜索樹算法,并在實際項目中發揮其優勢。謝謝閱讀!


希望這份博客能夠幫助到你。如果有其他需要修改或添加的地方,請隨時告訴我。

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

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

相關文章

共生型企業:駕馭AI自動化(事+AI)與人類增強(人+AI)的雙重前沿

目錄 引言&#xff1a;人工智能的雙重前沿 第一部分&#xff1a;自動化范式&#xff08;事AI&#xff09;——重新定義卓越運營 第一章&#xff1a;智能自動化的機制 第二章&#xff1a;自動化驅動的行業轉型 第三章&#xff1a;自動化的經濟演算 第二部分&#xff1a;協…

TypeScript的export用法

在 TypeScript 中&#xff0c;export 用于將模塊中的變量、函數、類、類型等暴露給外部使用。export 語法允許將模塊化的代碼分割并在其他文件中導入。 1. 命名導出&#xff08;Named Export&#xff09; 命名導出是 TypeScript 中最常見的一種導出方式&#xff0c;它允許你導出…

數據結構-2(鏈表)

一、思維導圖二、鏈表的反轉def reverse(self):"""思路&#xff1a;1、設置previous_node、current、next_node三個變量,目標是將current和previous_node逐步向后循環并逐步進行反轉,知道所有元素都被反轉2、但唯一的問題是&#xff1a;一旦current.next反轉為向…

ros2 標定相機

一個終端執行&#xff1a; ros2 run image_tools cam2image --ros-args -p width:640 -p height:480 -p frequency:30.0 -p device_id:-1 -r /image:/camera/image_raw另一個終端執行&#xff1a;8x6 是格子角點數量&#xff0c;0.028是格子尺寸 ros2 run camera_calibration …

IsaacLab學習記錄(二)

二、導入并訓練自己的機器人1、urdf等其他格式轉usd&#xff08;工具在./scrips/tools/&#xff09;???維度????URDF (Unified Robot Description Format)????USD (Universal Scene Description)????定位??機器人模型描述標準&#xff08;僅描述單機器人&…

基于Rust Softplus 函數實踐方法

Softplus 函數 Softplus 函數是神經網絡中常用的激活函數之一,定義為: ? Softplus函數導數 ? 是 sigmoid 函數。Softplus 處處可導,并且導數恰好是 sigmoid。 它是 ReLU 函數的平滑近似,具有連續可導的特性,適合需要梯度優化的場景。 數學特性 平滑性:導數為 Sig…

Ubuntu服務器安裝Miniconda

下載 Miniconda 安裝腳本&#xff08;如果能聯網&#xff09;wget https://repo.anaconda.com/miniconda/Miniconda3-py39_24.1.2-0-Linux-x86_64.sh -O Miniconda3.sh安裝 Miniconda 到 /opt/condabash Miniconda3.sh -b -p /opt/conda激活 conda/opt/conda/bin/conda init ba…

Java數組補充v2

一、數組基本概念1. 什么是數組數組是Java中用來存儲同類型數據的固定大小的連續內存空間的數據結構。2. 數組特點固定長度&#xff1a;一旦創建&#xff0c;長度不可改變相同類型&#xff1a;所有元素必須是同一數據類型索引訪問&#xff1a;通過下標&#xff08;從0開始&…

【PTA數據結構 | C語言版】前綴樹的3個操作

本專欄持續輸出數據結構題目集&#xff0c;歡迎訂閱。 文章目錄題目代碼題目 請編寫程序&#xff0c;利用前綴樹查找給定字符串是否在某給定字符串集合 S 中。 輸入格式&#xff1a; 輸入首先給出一個正整數 n&#xff08;≤1000&#xff09;&#xff0c;隨后 n 行&#xff0…

JAVA面試寶典 -《緩存架構:穿透 / 雪崩 / 擊穿解決方案》

&#x1f4a5;《緩存架構&#xff1a;穿透 / 雪崩 / 擊穿解決方案》 文章目錄&#x1f4a5;《緩存架構&#xff1a;穿透 / 雪崩 / 擊穿解決方案》&#x1f9ed; 一、開篇導語&#xff1a;為什么緩存是高并發系統的命脈&#xff1f;?1.1 緩存的核心價值緩存帶來的收益??&…

FPGA創意項目網頁或博客推薦

1. 綜合項目平臺(開源+教程) ① Hackster.io - FPGA專區 ?? https://www.hackster.io/fpga 特點: 大量基于FPGA的創意項目(如Zynq游戲機、視覺處理、機器人控制)。 提供完整教程(Vivado工程文件+代碼)。 推薦項目: FPGA-Based Oscilloscope(低成本示波器) V…

Go 程序無法使用 /etc/resolv.conf 的 DNS 配置排查記錄

在最近的一次部署中&#xff0c;我遇到一個奇怪的問題&#xff1a;Go 程序在運行時不使用 /etc/resolv.conf 中的 DNS 設置&#xff0c;導致服務無法正常訪問域名。這篇文章記錄下完整的排查過程和最終的解決方案。1. 問題現象我有一個部署在 KVM 虛擬機內的 Go 應用&#xff0…

微服務相關問題(2)

1、Spring Cloud相關常用組件注冊中心&#xff08;nacos、Eureka等&#xff09;、負載均衡&#xff08;Ribbon、LoadBalancer&#xff09;、遠程調用&#xff08;feign&#xff09;、服務熔斷&#xff08;Sentinel、Hystrix&#xff09;、網關&#xff08;Gateway&#xff09;2…

安全初級2

一、作業要求 1、xss-labs 1~8關 2、python實現自動化sql布爾育注代碼優化(二分查找) 二、xss-labs 1~8關 1、準備 打開小皮面板&#xff0c;啟動MySQL和apacher 下載 xss-labs&#xff0c;并解壓后放到 phpstudy_pro 的 WWW 目錄下&#xff0c;重命名為 xss-labs 訪問鏈…

基礎算法題

基礎算法題 鏈表 1.1反轉鏈表 描述&#xff1a; 描述 給定一個單鏈表的頭結點pHead(該頭節點是有值的&#xff0c;比如在下圖&#xff0c;它的val是1)&#xff0c;長度為n&#xff0c;反轉該鏈表后&#xff0c;返回新鏈表的表頭。 數據范圍&#xff1a; 0≤&#xfffd;≤…

Android 15 源碼修改:為第三方應用提供截屏接口

概述 在 Android 系統開發中,有時需要為第三方應用提供系統級的截屏功能。本文將詳細介紹如何通過修改 Android 15 源碼中的 PhoneWindowManager 類,實現一個自定義廣播接口來觸發系統截屏功能。 修改方案 核心思路 通過在系統服務 PhoneWindowManager 中注冊自定義廣播監…

20250717 Ubuntu 掛載遠程 Windows 服務器上的硬盤

由 DeepSeek 生成&#xff0c;方法已經驗證可行。 通過網絡掛載Windows共享硬盤&#xff08;SMB/CIFS&#xff09; 確保網絡共享已啟用&#xff1a; 在Windows電腦上&#xff0c;右鍵點擊目標硬盤或文件夾 → 屬性 → 共享 → 啟用共享并設置權限&#xff08;至少賦予讀取權限&…

深度學習圖像增強方法(二)

三、直方圖均衡化 1. 普通直方圖均衡化 直方圖均衡化的原理是將圖像的灰度直方圖展平,使得每個灰度級都有更多的像素分布,從而增強圖像的對比度。具體步驟如下: 計算灰度直方圖:統計圖像中每個灰度級的像素數量。 計算累積分布函數(CDF):計算每個灰度級的累積概率。 映…

QT——信號與槽/自定義信號與槽

1 信號與槽基本介紹 提出疑問&#xff0c;界面上已經有按鍵了&#xff0c;怎么操作才能讓用戶按下按鍵后有操作上的反應呢&#xff1f; 在 Qt 中&#xff0c;信號和槽機制是一種非常強大的事件通信機制。這是一個重要的概念&#xff0c;特別是對于初學者來說&#xff0c;理解它…

Spring原理揭秘--Spring的AOP

在這之前我們已經介紹了AOP的基本功能和概念&#xff0c;那么當AOP集成到spring則會發生改變。Spring AOP 中的Joinpoint&#xff1a;之前提高了很多Joinpoint的類型&#xff0c;但是在spring中則只會有方法級別的Joinpoint&#xff0c;像構造方法&#xff0c;字段的調用都沒適…