669. 修剪二叉搜索樹 : 常規樹的遍歷與二叉樹性質
題目描述
這是 LeetCode 上的 669. 修剪二叉搜索樹 ,難度為 中等 。
Tag : 「BST」、「二叉樹」、「遞歸」、「迭代」
給你二叉搜索樹的根節點 root
,同時給定最小邊界 low
和最大邊界 high
。通過修剪二叉搜索樹,使得所有節點的值在 $[low, high]$ 中。修剪樹 不應該 改變保留在樹中的元素的相對結構 (即,如果沒有被移除,原有的父代子代關係都應當保留)。 可以證明,存在 唯一的答案 。
所以結果應當返回修剪好的二叉搜索樹的新的根節點。注意,根節點可能會根據給定的邊界發生改變。
示例 1:

輸入:root = [1,0,2], low = 1, high = 2 輸出:[1,null,2]
示例 2:

輸入:root = [3,0,4,null,2,null,null,1], low = 1, high = 3 輸出:[3,2,null,1]
提示:
- 樹中節點數在範圍 $[1, 10^4]$ 內
- $0 <= Node.val <= 10^4$
- 樹中每個節點的值都是 唯一 的
- 題目數據保證輸入是一棵有效的二叉搜索樹
- $0 <= low <= high <= 10^4$
遞歸
由於被修剪的是二叉搜索樹,因此修剪過程必然能夠順利進行。
容易想到使用原函數作為遞歸函數:
- 若
root.val
小於邊界值low
,則root
的左子樹必然均小於邊界值,我們遞歸處理root.right
即可; - 若
root.val
大於邊界值high
,則root
的右子樹必然均大於邊界值,我們遞歸處理root.left
即可; - 若
root.val
符合要求,則root
可被保留,遞歸處理其左右節點並重新賦值即可。
Java 代碼:
class Solution { public TreeNode trimBST(TreeNode root, int low, int high) { if (root == null) return null; if (root.val < low) return trimBST(root.right, low, high); else if (root.val > high) return trimBST(root.left, low, high); root.left = trimBST(root.left, low, high); root.right = trimBST(root.right, low, high); return root; } }
TypeScript 代碼:
function trimBST(root: TreeNode | null, low: number, high: number): TreeNode | null { if (root == null) return null if (root.val < low) return trimBST(root.right, low, high) else if (root.val > high) return trimBST(root.left, low, high) root.left = trimBST(root.left, low, high) root.right = trimBST(root.right, low, high) return root };
- 時間複雜度:$O(n)$
- 空間複雜度:忽略遞歸帶來的額外空間開銷,複雜度為 $O(1)$
迭代
自然能夠使用「迭代」進行求解:起始先從給定的 root
進行出發,找到第一個滿足值符合 $[low, high]$ 範圍的節點,該節點為最後要返回的根節點 ans
。
隨後考慮如何修剪 ans
的左右節點:當根節點符合 $[low, high]$ 要求時,修剪左右節點過程中僅需考慮一邊的邊界值即可。即對於 ans.left
只需考慮將值小於 low
的節點去掉(因為二叉搜索樹的特性, ans
滿足不大於 high
要求,則其左節點必然滿足);同理 ans.right
只需要考慮將大於 high
的節點去掉即可。
Java 代碼:
class Solution { public TreeNode trimBST(TreeNode root, int low, int high) { while (root != null && (root.val < low || root.val > high)) root = root.val < low ? root.right : root.left; TreeNode ans = root; while (root != null) { while (root.left != null && root.left.val < low) root.left = root.left.right; root = root.left; } root = ans; while (root != null) { while (root.right != null && root.right.val > high) root.right = root.right.left; root = root.right; } return ans; } }
TypeScript 代碼:
function trimBST(root: TreeNode | null, low: number, high: number): TreeNode | null { while (root != null && (root.val < low || root.val > high)) root = root.val < low ? root.right : root.left const ans = root while (root != null) { while (root.left != null && root.left.val < low) root.left = root.left.right root = root.left } root = ans while (root != null) { while (root.right != null && root.right.val > high) root.right = root.right.left root = root.right } return ans };
- 時間複雜度:$O(n)$
- 空間複雜度:$O(1)$
最後
這是我們「刷穿 LeetCode」系列文章的第 No.669
篇,系列開始於 2021/01/01,截止於起始日 LeetCode 上共有 1916 道題目,部分是有鎖題,我們將先把所有不帶鎖的題目刷完。
在這個系列文章裏面,除了講解解題思路以外,還會盡可能給出最為簡潔的代碼。如果涉及通解還會相應的代碼模板。
為了方便各位同學能夠電腦上進行調試和提交代碼,我建立了相關的倉庫: http://github.com/SharingSou... 。
在倉庫地址裏,你可以看到系列文章的題解鏈接、系列文章的相應代碼、LeetCode 原題鏈接和其他優選題解。
更多更全更熱門的「筆試/面試」相關資料可訪問排版精美的合集新基地 :tada::tada:
本文由mdnice多平台發佈
- 天翼雲全場景業務無縫替換至國產原生操作系統CTyunOS!
- 以羊了個羊為例,淺談小程序抓包與響應報文修改
- 這幾種常見的 JVM 調優場景,你知道嗎?
- 如此狂妄,自稱高性能隊列的Disruptor有啥來頭?
- 為什麼要學習GoF設計模式?
- 827. 最大人工島 : 簡單「並查集 枚舉」運用題
- 手把手教你如何使用 Timestream 實現物聯網時序數據存儲和分析
- 850. 矩形面積 II : 掃描線模板題
- Java 併發編程解析 | 基於JDK源碼解析Java領域中的併發鎖,我們可以從中學習到什麼內容?
- 【手把手】光説不練假把式,這篇全鏈路壓測實踐探索
- 大廠鍾愛的全鏈路壓測有什麼意義?四種壓測方案詳細對比分析
- 寫個續集,填坑來了!關於“Thread.sleep(0)這一行‘看似無用’的代碼”裏面留下的坑。
- 857. 僱傭 K 名工人的最低成本 : 枚舉 優先隊列(堆)運用題
- Vue3 實現一個自定義toast(小彈窗)
- 669. 修剪二叉搜索樹 : 常規樹的遍歷與二叉樹性質
- 讀完 RocketMQ 源碼,我學會了如何優雅的創建線程
- 性能調優——小小的log大大的坑
- 1582. 二進制矩陣中的特殊位置 : 簡單模擬題
- elementui源碼學習之仿寫一個el-switch
- 646. 最長數對鏈 : 常規貪心 DP 運用題