今天分享的題目來源於 LeetCode 第 450 號問題:刪除二叉搜索樹中的節點。雖然它的難度是 中等,但實際上很好理解,請往下看!
題目描述
給定一個二叉搜索樹的根節點 root 和一個值 key,刪除二叉搜索樹中的 key 對應的節點,並保證二叉搜索樹的性質不變。返回二叉搜索樹(有可能被更新)的根節點的引用。
一般來說,刪除節點可分爲兩個步驟:
-
首先找到需要刪除的節點;
-
如果找到了,刪除它。
說明:要求算法時間複雜度爲 O(h),h 爲樹的高度。
示例:
root = [5,3,6,2,4,null,7]
key = 3
5
/ \
3 6
/ \ \
2 4 7
給定需要刪除的節點值是 3,所以我們首先找到 3 這個節點,然後刪除它。
一個正確的答案是 [5,4,6,2,null,null,7], 如下圖所示。
5
/ \
4 6
/ \
2 7
另一個正確答案是 [5,2,6,null,4,null,7]。
5
/ \
2 6
\ \
4 7
題目解析
在二叉搜索樹上刪除一個節點,這道題目有一個隱含的條件,就是樹上節點的值不重複。
另外題目還要求時間複雜度需要保證 O(h)
這裏的 h 表示的是二叉樹的高度。
其實這個題目是分成兩個步驟的,第一個是找到對應的節點,第二個是刪除節點。
因爲是二叉搜索樹,對於樹上每個節點來說,其 右子樹的節點都要大於其左子樹的節點,那麼要找對應節點,我們可以從根節點開始,一路比較,大的話就去右邊找,小的話就去左邊找,這樣每次我們都往下,可以保證時間複雜度是 O(h)
。
當我們找到了要刪除的節點,在刪除這一步就會有很多的細節,主要是因爲我們需要調整餘下的結構,以維持二叉搜索樹的性質。
針對這個問題,我們可以分情況討論:
5
/ \
3 6
/ \ \
2 4 7
/ \
1 8
-
情況 1:當刪除的節點沒有左右子樹,比如上圖的 4、8、1
這時直接刪除即可,樹依舊可以保持二叉搜索樹的性質 -
情況 2:當刪除的節點有左子樹沒有右子樹,比如上圖的 2
這時我們只需要將整個左子樹移到當前位置即可
也就是將左子樹的根節點放到刪除節點的位置,其餘不變 -
情況 3:當刪除的節點沒有左子樹有右子樹,比如上圖的 6、7
這時我們只需要將整個右子樹移到當前位置即可
也就是將右子樹的根節點放到刪除節點的位置,其餘不變 -
情況 4:當刪除的節點既有左子樹又有右子樹,比如上圖的 5、3
這時就有兩種方法供選擇:
去到左子樹中,找到值最大節點,將右子樹全部移到這個節點下
去到右子樹中,找到值最小節點,將左子樹全部移到這個節點下
通過上面的討論分析,我們有了大致的思路。在實現方面,我們可以藉助遞歸來巧妙地達到刪除對應節點的目的。
圖片描述
image
image
image
image
image
image
image
image
image
image
參考代碼
//五分鐘學算法
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) {
return null;
}
// 當前遍歷到的節點大於要找的節點,去左邊繼續找
if (root.val > key) {
root.left = deleteNode(root.left, key);
}
// 當前遍歷到的節點小於要找的節點,去右邊繼續找
else if (root.val < key) {
root.right = deleteNode(root.right, key);
}
// 找到要刪除的節點,進行刪除操作
else {
// 情況 1 & 2
if (root.right == null) {
return root.left;
}
// 情況 3
if (root.left == null) {
return root.right;
}
// 去到刪除節點的右子樹,找到值最小的節點
TreeNode rightSmallest = root.right;
while (rightSmallest.left != null) {
rightSmallest = rightSmallest.left;
}
// 將刪除節點的左子樹全部移到這個節點下
rightSmallest.left = root.left;
// 返回右子樹的根節點,放到當前刪除節點的位置
return root.right;
}
return root;
}
作者:碼農小光
鏈接:https://www.jianshu.com/p/89a199fc75e5
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。