題目介紹
描述:
設計一個算法,找出二叉搜索樹中指定節點的“下一個”節點(也即中序後繼)。
如果指定節點沒有對應的“下一個”節點,則返回null
。
示例 1:
輸入: root = [2,1,3], p = 1
2
/ \\
1 3
輸出: 2
示例 2:
輸入: root = [5,3,6,2,4,null,null,1], p = 6
5
/ \\
3 6
/ \\
2 4
/
1
輸出: null
解題思路:
遞歸算法的關鍵是要明確函數的「定義」是什麼,然後相信這個定義,利用這個定義推導最終結果。
寫樹相關的算法,簡單說就是,先搞清楚當前 root 節點該做什麼,然後根據函數定義遞歸調用子節點,遞歸調用會讓孩子節點做相同的事情。
二叉樹題目的一個難點在於如何通過題目的要求思考出每一個節點需要做什麼
二叉樹解題策略
一 遞歸 二 隊列 + 迭代 (層次遍歷) 三 棧 + 迭代 (非遞歸遍歷) 四 其它
三種基本的遍歷方式,都可以用遞歸來實現。寫遞歸算法的時候,需要注意遞歸退出條件以及遞歸操作的表達。
自己的解法實現
def inorderSuccessor(self, root, p):
if not root or not p: return None
flag, res = False, None
def helper(node):
nonlocal flag, res
if node and not res:
helper(node.left)
if node is p:
flag = True
elif flag:
res, flag = node, False
helper(node.right)
helper(root)
return res
網上比較優秀的解法
解法一
BST+遞歸 首先本題中的二叉樹還是個二叉搜索樹,也就是中序遍歷是單調遞增的,所以我們可以利用這個性質來簡化查找過程。
如果結點 p 的值大於等於 root 的值,說明 p 的後繼結點在 root 右子樹中,那麼就遞歸到右子樹中查找。 如果結點 p 的值小於 root 的值,說明 p 在 root 左子樹中,而它的後繼結點有兩種可能,要麼也在左子樹中,要麼就是 root: 如果左子樹中找到了後繼結點,那就直接返回答案。 如果左子樹中沒有找到後繼結點,那就說明 p 的右兒子爲空,那麼 root 就是它的後繼結點。
def inorderSuccessor2(self, root, p):
if not root: return None
if p.val >= root.val:
return self.inorderSuccessor(root.right, p)
else:
left = self.inorderSuccessor(root.left, p)
return left if left else root
解法二
BST+非遞歸 如果 p 有右兒子,那麼它的後繼結點就是右子樹的最左邊的兒子。 如果 p 沒有右兒子,那麼它的後繼結點就是,沿着 p 往上到 root 的路徑中,第一個左兒子在路徑上的結點。因爲這個結點的左子樹中 p 是最右邊的結點,是最大的,所以它就是 p 的後繼結點。因爲是二叉搜索樹,我們就可以從根結點開始往 p 走,根據結點值的大小決定走的方向。
def inorderSuccessor3(self, root, p):
if not root or not p: return None
if p.right:
p = p.right
while p.left:
p = p.left
return p
res = TreeNode(None)
while root != p:
if root.val < p.val:
root = root.right
else:
res = root
root = root.left
return res
解法三
所謂 p 的後繼節點,就是這串升序數字中,比 p 大的下一個。
如果 p 大於當前節點的值,說明後繼節點一定在 RightTree 如果 p 等於當前節點的值,說明後繼節點一定在 RightTree 如果 p 小於當前節點的值,說明後繼節點一定在 LeftTree 或自己就是 遞歸調用 LeftTree,如果是空的,說明當前節點就是答案 如果不是空的,則說明在 LeftTree 已經找到合適的答案,直接返回即可
def inorderSuccessor5(self, root, p):
if not root or not p: return None
if root:
if p.val >= root.val:
return self.inorderSuccessor(root.right, p)
else:
if self.inorderSuccessor(root.left, p) is None:
return root
else:
return self.inorderSuccessor(root.left, p)
return None
相關知識總結和思考
相關知識:
BFS:廣度/寬度優先。其實就是從上到下,先把每一層遍歷完之後再遍歷一下一層。
可以使用Queue的數據結構。我們將root節點初始化進隊列,通過消耗尾部,插入頭部的方式來完成BFS。
二叉搜索樹(BST)的特性:
- 若它的左子樹不爲空,則所有左子樹上的值均小於其根節點的值
- 若它的右子樹不爲空,則所有右子樹上的值均大於其根節點的值
- 它的左右子樹也分別爲二叉搜索樹
遞歸與迭代的區別
遞歸:重複調用函數自身實現循環稱爲遞歸; 迭代:利用變量的原值推出新值稱爲迭代,或者說迭代是函數內某段代碼實現循環;