Description
給定一棵 BST 的根節點以及範圍 [low, high],修剪 BST 使得所有節點的值都在 [low, high] 範圍內。修剪後的樹仍須保持 BST 性質。
Example:
Input: root = [1,0,2], low = 1, high = 2 Output: [1,null,2]
Intuition#
利用 BST 性質:節點值太小就往右找,太大就往左找,範圍內就遞迴修剪左右子樹。
- 如果當前節點值 < low,整個左子樹都不要,答案在右子樹中
- 如果當前節點值 > high,整個右子樹都不要,答案在左子樹中
- 如果在範圍內,遞迴修剪左右子樹
Approaches#
1: 遞迴#
- 概念: 利用 BST 性質遞迴修剪,超出範圍的子樹直接跳過
- 時間複雜度:
O(n) - 空間複雜度:
O(h),h 為樹高
class Solution {
fun trimBST(root: TreeNode?, low: Int, high: Int): TreeNode? {
if (root == null) return null
if (root.`val` < low) return trimBST(root.right, low, high)
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
}
}⭐ 2: 迭代#
- 概念: 先找到根節點在範圍內,再分別處理左右子樹的越界節點
- 時間複雜度:
O(n) - 空間複雜度:
O(1)
class Solution {
fun trimBST(root: TreeNode?, low: Int, high: Int): TreeNode? {
// 找到在範圍內的根
var node = root
while (node != null && (node.`val` < low || node.`val` > high)) {
node = if (node.`val` < low) node.right else node.left
}
// 修剪左子樹中 < low 的部分
var curr = node
while (curr != null) {
while (curr.left != null && curr.left!!.`val` < low) {
curr.left = curr.left!!.right
}
curr = curr.left
}
// 修剪右子樹中 > high 的部分
curr = node
while (curr != null) {
while (curr.right != null && curr.right!!.`val` > high) {
curr.right = curr.right!!.left
}
curr = curr.right
}
return node
}
}🔑 Takeaways#
- Pattern: BST 的修剪利用有序性質,超出範圍的節點可以直接跳到另一側子樹
- 關鍵技巧: 遞迴解法三行判斷非常簡潔:太小往右、太大往左、範圍內遞迴兩側