题目介绍
描述:
设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。
如果指定节点没有对应的“下一个”节点,则返回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)的特性:
- 若它的左子树不为空,则所有左子树上的值均小于其根节点的值
- 若它的右子树不为空,则所有右子树上的值均大于其根节点的值
- 它的左右子树也分别为二叉搜索树
递归与迭代的区别
递归:重复调用函数自身实现循环称为递归; 迭代:利用变量的原值推出新值称为迭代,或者说迭代是函数内某段代码实现循环;