98. 验证二叉搜索树 https://leetcode-cn.com/problems/validate-binary-search-tree/
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
解:
中序遍历二叉排序树,判断得到的数组是否是升序。
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def isValidBST(self, root: TreeNode) -> bool: inorder = self.inorder(root) return inorder == sorted(set(inorder)) # 这里要注意判重 def inorder(self, root): if root is None: return [] return self.inorder(root.left) + [root.val] + self.inorder(root.right)
但是这个效率是相对较低的,想办法改进一下,不用完整保留中序遍历的结果,只需要比较中序遍历时当前节点和前驱节点即可
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def isValidBST(self, root: TreeNode) -> bool: self.prev = None return self.helper(root) def helper(self, root): if root is None: return True if not self.helper(root.left): # 如果左子树不是二叉搜索树,那root也不是 return False if self.prev and self.prev.val >= root.val: # 如果左子树是二叉搜索树,比较root和其前驱节点值,如果前驱大,root不是二叉搜索树 return False self.prev = root # 如果满足root比前驱大,下面去检查右子树,在此之前要先把前驱节点更新为root return self.helper(root.right)
直接递归来判断
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def isValidBST(self, root: TreeNode) -> bool: if root is None: return True if root.left is None and root.right is None: return True flag = True if root.left: if not self.isValidBST(root.left): flag = False # 左子树的最大,一直向右即可 l = root.left while l.right: l = l.right if root.val <= l.val: flag = False if root.right: if not self.isValidBST(root.right): flag = False # 右子树的最小,一直向左即可 r = root.right while r.left: r = r.left if root.val >= r.val: flag = False return flag
这个递归写的也是比较冗余的,有很多重复搜索。再简化一下,每次都把节点和其上下界比较(如果有的话)。
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def isValidBST(self, root: TreeNode) -> bool: return self.isValid(root) def isValid(self, root, lower=float('-inf'), upper=float('inf')): if root is None: return True val = root.val if lower is not None and val <= lower: return False if upper is not None and val >= upper: return False if not self.isValid(root.left, lower, val): return False if not self.isValid(root.right, val, upper): return False return True
235. 二叉搜索树的最近公共祖先 https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
说明:
所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉搜索树中。
解:
迭代,从根节点往下,如果根节点比两个节点都大,说明两个节点在根节点的左子树中;如果根节点比两个节点都小,说明两个节点在根节点的右子树中。更新根节点即可。
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': if not root: return None if root.left is None and root.right is None: return root ans = root while ans: if ans.val < p.val and ans.val < q.val: ans = ans.right elif ans.val > p.val and ans.val > q.val: ans = ans.left else: return ans
递归实现,空间复杂度O(N)。这题其实没必要用递归
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': if root.val < p.val and root.val < q.val: return self.lowestCommonAncestor(root.right, p, q) # p、q比root都大,需要去root右子树查找 if root.val > p.val and root.val > q.val: return self.lowestCommonAncestor(root.left, p, q)
return root
236.二叉树的最近公共祖先 https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
解:
递归,从根节点开始向下,如果root就是p或者q,那么公共祖先就是root,否则的话分别去左右子树查找p和q,如果p、q都在左子树,递归的去左子树向下查找;如果p、q都在右子树,递归的去右子树查找;如果p、q各在一边,那么root
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': if root is None or root is q or root is p: return root left = self.lowestCommonAncestor(root.left, p, q) # 分别去左右子树查找p和q right = self.lowestCommonAncestor(root.right, p, q) if left is None: # 如果p、q都不在左子树,返回右子树的查找结果 return right elif right is None: # 如果p、q都不在右子树,返回左子树的查找结果 return left return root # 如果p和q各在左右一边,公共祖先就是当前root
使用父指针迭代,找出 p 和 q 的各自到根节点的路径。如果每个节点都有父指针,可以从 p 和 q 返回以获取它们的祖先。在这个遍历过程中,得到的第一个公共节点是 LCA 节点。可以在遍历树时将父指针保存在字典中。
# Definition for a binary tree node. # class TreeNode: # def __init__(self, x): # self.val = x # self.left = None # self.right = None class Solution: def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode': parent = {root: None} stack = [root] # 只要没有把p、q都找到,就继续向下遍历树,继续找 while p not in parent or q not in parent: node = stack.pop() if node.left: parent[node.left] = node stack.append(node.left) if node.right: parent[node.right] = node stack.append(node.right) ancestors = set() # 按父指针遍历p的祖先 while p: ancestors.add(p) p = parent[p] while q not in ancestors: # 找p、q公共的祖先 q = parent[q] return q