听首歌吧http://
本题题干给出是BST,所以需要好好利用这个性质。
首先,如果一个节点是共同祖先,那么首先要满足值在[p, q]内,才能成为根节点。
但是,满足这个情况的节点可能有很多,但是最近的只有一个。比如下图:
通过观察可以发现,如果从最顶端开始遍历,那么第一个落在[p, q]区间内的节点就是我们要找的共同祖先。
返回值的确定:由于是找到一个就返回,不需要联合left和right的逻辑,所以返回值为当前节点。
顺序:自上而下,大于p、q进左子树;小于p、q进右子树。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
return self.traversal(root, p, q)
def traversal(self, node, p, q):
# 确定终止条件
if not node:
return node
# 如果当前节点大于p和q:去左子树
if node.val > p.val and node.val > q.val:
left = self.traversal(node.left, p, q)
if left is not None: # 如果左子树找到了区间内的第一个点,返回
return left
# 如果当前节点小于p和q:去右子树
elif node.val < p.val and node.val < q.val:
right = self.traversal(node.right, p, q)
if right is not None: # 找到区间内的第一个点,返回
return right
return node # 找到一个就返回,不需要遍历整个树,return需要返回值
迭代更适合BST的遍历搜索。遍历顺序和递归是一致的。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution(object):
def lowestCommonAncestor(self, root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
# 迭代
while root:
if root.val > p.val and root.val > q.val:
root = root.left
elif root.val < p.val and root.val < q.val:
root = root.right
else:
return root
return None
BST的一个性质:插入值都可以落在叶子节点(不重复的情况下)
利用上面这个性质,就变得十分甚至九分的简单:小于root进左子树;大于root进右子树。遇到空节点,创建新node。
插入的一个好处是不会改变已有的结构,只需要开花结果即可。
本题也是属于找到(构造)好就直接回溯返回根节点的,所以返回值是node,中间处理的时候需要left和right分别接住左右子树的返回值。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def insertIntoBST(self, root, val):
"""
:type root: TreeNode
:type val: int
:rtype: TreeNode
"""
node = root
if node is None:
node = TreeNode(val)
return node
# 中间处理:小于当前节点,进左子树
if node.val > val:
node.left = self.insertIntoBST(node.left, val)
# 大于当前节点,进右子树
elif node.val < val:
node.right = self.insertIntoBST(node.right, val)
return node # 找到(构造)一个满足条件的即返回,所以返回值是node
迭代可以参考上一片BST的迭代写法。这样写的好处是可以直接按照顺序从上往下遍历。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def insertIntoBST(self, root, val):
"""
:type root: TreeNode
:type val: int
:rtype: TreeNode
"""
# 迭代
if root is None:
root = TreeNode(val)
return root
cur = root
# while 结束的时候cur停在和val并列的一层
while cur:
pre = cur
if cur.val > val:
cur = cur.left
else:
cur = cur.right
node = TreeNode(val)
# 需要用上一层的节点来指向val节点
if node.val < pre.val:
pre.left = node
else:
pre.right = node
return root
和上面的添加节点只用在叶子节点添加不同,这里我们需要改变树的内部结构,类似链表的删除(虽然二叉树默认是链表形式储存的)。
没有该节点 | 返回None |
是叶子节点(左右都是空节点) | 直接删除 |
是中间节点,左空右不空 | 删除+衔接 |
是中间节点,左不空右空 | 删除+衔接 |
是中间节点,左右都不空 | 右子树继承+嫁接左子树 |
那么对应上面的每一种情况我们就可以确定递归函数的终止条件。
最后一种的删除方式可以参考下图:
这里使用比较一般的递归解法,逐步实现每一种情况的分解。
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def deleteNode(self, root, key):
"""
:type root: TreeNode
:type key: int
:rtype: TreeNode
"""
# <1> 没找到节点
if not root:
return None
if root.val == key:
# <2> 找到的是叶子节点
if root.left == None and root.right == None:
return None
# <3> 中间节点没有左子树
elif root.left == None:
return root.right
# <4> 中间节点没有右子树
elif root.right == None:
return root.left
# <5> 中间节点左右子树都有
else:
cur = root.right # 从右子树开始
# 找到比左子树根节点大的第一个数,也就是右子树的最左端
while cur.left:
cur = cur.left
# 原左子树嫁接到原右子树的最左端
cur.left = root.left
# 右子树继承原根节点
return root.right
# 递归
if root.val > key: # 小于走左子树
root.left = self.deleteNode(root.left, key)
elif root.val < key: # 大于走右子树
root.right = self.deleteNode(root.right, key)
return root
第22天完成