1. 二叉搜索树最近公共祖先(leetcode 235)
迭代法
这题与寻找二叉树的最近公共祖先不一样,我们可以利用二叉搜索树的特性,看当前节点与pq的值的大小关系,然后不停往正确的道路迭代,最终就能找到公共祖先
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
while root:
if p.val > root.val and q.val > root.val:
root = root.right
elif p.val < root.val and q.val < root.val:
root = root.left
elif p.val > root.val and q.val < root.val:
return root
elif p.val < root.val and q.val > root.val:
return root
elif p.val == root.val:
return p
elif q.val == root.val:
return q
处理好每种情况就行了
递归法:
本题是二叉搜索树,二叉搜索树是有序的,那得好好利用一下这个特点。
在有序树里,如果判断一个节点的左子树里有p,右子树里有q呢?
因为是有序树,所以 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p。
那么只要从上到下去遍历,遇到 cur节点是数值在[p, q]区间中则一定可以说明该节点cur就是p 和 q的公共祖先。 那问题来了,一定是最近公共祖先吗?
如图,我们从根节点搜索,第一次遇到 cur节点是数值在[q, p]区间中,即 节点5,此时可以说明 q 和 p 一定分别存在于 节点 5的左子树,和右子树中。
递归三部曲:
1. 确认递归值和参数:参数为当前节点,p,q。返回值为最近公共祖先
2. 终止条件:遇到空节点返回
3. 确定单层递归逻辑:比大小顺着路走。走完了以后直接return root。
完整代码:
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root.val > p.val and root.val > q.val:
left = self.lowestCommonAncestor(root.left, p, q)
if left is not None:
return left
if root.val < p.val and root.val < q.val:
right = self.lowestCommonAncestor(root.right, p, q)
if right is not None:
return right
return root
2. 二叉搜索树中的插入(leetcode 701)
我们可以用迭代法,对比val和root的大小,遍历到最接近val的叶子,然后在结尾加上他
class Solution:
def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
if not root:
return TreeNode(val)
current = root
parent = root
while current:
if val > current.val:
parent = current
current = current.right
elif val < current.val:
parent = current
current = current.left
if val > parent.val:
parent.right = TreeNode(val)
elif val < parent.val:
parent.left = TreeNode(val)
return root
但因为你需要两个指针来记录中间遍历的母子,所以还是比较花时间的
递归法也是一样的道理
class Solution:
def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
if not root:
return TreeNode(val)
if root.val > val:
root.left = self.insertIntoBST(root.left, val)
elif root.val <val:
root.right = self.insertIntoBST(root.right, val)
return root
3. 删除二叉搜索树中的节点(leetcode 450)
我们在删除节点主要有两个步骤
首先我们要找到目标节点,找到了再删除。
首先我们先通过递归函数来寻找节点,然后再讨论删除的具体操作
递归三部曲:首先先确认我们用前序遍历。
1. 确定参数以及返回值:参数就是当前节点
2. 终止条件:如果遇到空节点也没找到删除的节点,直接返回root
3. 确定单层递归逻辑
如果没找到target,遍历到空节点我们就返回了
当我们遇到要删除的节点时,一共有一下几种情况
- 第一种,左右孩子都为空,直接删除,返回NULL
-第二种,左孩子为空,右孩子不为空,直接返回右孩子
-第三种,右孩子为空,左孩子不为空,返回左孩子
-第四种,左右孩子都不为空:我们可以将删除节点的左孩子整颗子树放到右孩子最左边的叶节点的左边,并返回右孩子成为新的节点。
具体步骤如图。(图源代码随想录)
这道题的关键就在于当左右孩子都存在的情况下,我们要怎么删除节点。要注意运用二叉搜索树的性质。
class Solution:
def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
if root is None:
return root
if root.val == key:
if not root.left and not root.right:
return None
elif not root.left and root.right:
return root.right
elif not root.right and root.left:
return root.left
elif root.left and root.right:
cur = root.right
while cur.left is not None:
cur = cur.left
cur.left = root.left
return root.right
#这里相当于把新的节点返回给上一层,上一层就要用 root->left 或者 root->right接住
if root.val > key:
root.left = self.deleteNode(root.left, key)
if root.val < key:
root.right = self.deleteNode(root.right, key)
return root