利用二叉搜索树的特性,不再需要后序遍历,因为不再需要回溯,利用二叉搜索树的特性,从上至下,前序遍历即可。
一旦遇到了一个节点,其值在 p 和 q 之间,那么这个节点一定是最近公共祖先,因为 p 在其左 , q 在其右,再往下走一步,不管是向左还是向右,一定会错过 p 或 q 中的一个。
理解清楚这点很重要!
递归法(版本一)
class Solution:
def traversal(self, cur, p, q):
if cur is None:
return cur
# 中
if cur.val > p.val and cur.val > q.val: # 左
left = self.traversal(cur.left, p, q)
if left is not None:
return left
if cur.val < p.val and cur.val < q.val: # 右
right = self.traversal(cur.right, p, q)
if right is not None:
return right
return cur
def lowestCommonAncestor(self, root, p, q):
return self.traversal(root, p, q)
迭代法(版本二)精简
class Solution:
def lowestCommonAncestor(self, root, p, q):
if root.val > p.val and root.val > q.val:
return self.lowestCommonAncestor(root.left, p, q)
elif root.val < p.val and root.val < q.val:
return self.lowestCommonAncestor(root.right, p, q)
else:
return root
迭代法
class Solution:
def lowestCommonAncestor(self, root, p, q):
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
不知道写的好不好?和版本一在逻辑上差不多。
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root == None:
return None
if root.val == p.val or root.val == q.val :
return root
bool_p = root.val > p.val
bool_q = root.val > q.val
if bool_p and bool_q :
return self.lowestCommonAncestor(root.left,p,q)
elif not bool_p and not bool_q :
return self.lowestCommonAncestor(root.right,p,q)
else :
return root
这道题目其实是一道简单题目,但是题目中的提示:有多种有效的插入方式,还可以重构二叉搜索树,一下子吓退了不少人,瞬间感觉题目复杂了很多。
其实可以不考虑题目中提示所说的改变树的结构的插入方式。
其实:只要按照二叉搜索树的规则去遍历,遇到空节点就插入节点就可以了。
就直接往叶子节点插入,不考虑别的。
不要把题目想复杂了,这就是一颗二叉搜索树而已,又不是平衡二叉树,要调整左右子树的高度等。
递归法(版本一)
class Solution:
def __init__(self):
self.parent = None
def traversal(self, cur, val):
if cur is None:
node = TreeNode(val)
if val > self.parent.val:
self.parent.right = node
else:
self.parent.left = node
return
self.parent = cur
if cur.val > val:
self.traversal(cur.left, val)
if cur.val < val:
self.traversal(cur.right, val)
def insertIntoBST(self, root, val):
self.parent = TreeNode(0)
if root is None:
return TreeNode(val)
self.traversal(root, val)
return root
递归法(版本二)
class Solution:
def insertIntoBST(self, root, val):
if root is None:
return TreeNode(val)
parent = None
cur = root
while cur:
parent = cur
if val < cur.val:
cur = cur.left
else:
cur = cur.right
if val < parent.val:
parent.left = TreeNode(val)
else:
parent.right = TreeNode(val)
return root
递归法(版本三)
class Solution:
def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
if root is None or root.val == val:
return TreeNode(val)
elif root.val > val:
if root.left is None:
root.left = TreeNode(val)
else:
self.insertIntoBST(root.left, val)
elif root.val < val:
if root.right is None:
root.right = TreeNode(val)
else:
self.insertIntoBST(root.right, val)
return root
递归法(版本四)
class Solution:
def insertIntoBST(self, root, val):
if root is None:
node = TreeNode(val)
return node
if root.val > val:
root.left = self.insertIntoBST(root.left, val)
if root.val < val:
root.right = self.insertIntoBST(root.right, val)
return root
迭代法
class Solution:
def insertIntoBST(self, root, val):
if root is None: # 如果根节点为空,创建新节点作为根节点并返回
node = TreeNode(val)
return node
cur = root
parent = root # 记录上一个节点,用于连接新节点
while cur is not None:
parent = cur
if cur.val > val:
cur = cur.left
else:
cur = cur.right
node = TreeNode(val)
if val < parent.val:
parent.left = node # 将新节点连接到父节点的左子树
else:
parent.right = node # 将新节点连接到父节点的右子树
return root
class Solution:
def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
if root == None :
return TreeNode(val)
self.digui(root,val)
return root
def digui(self,root,val):
if root == None :
node = TreeNode(val)
return node
if root.val > val :
left = self.digui(root.left,val)
if left != None :
root.left = left
if root.val < val :
right = self.digui(root.right,val)
if right != None :
root.right = right
如果理解了递归解法的版本四,那么上述的各种判断就不用写那么多了,如果Left不做更改,那么root.left更新的时候,就是原树的root.left。
class Solution:
def insertIntoBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
if root == None :
node = TreeNode(val)
return node
if root.val > val :
left = self.insertIntoBST(root.left,val)
root.left = left
if root.val < val :
right = self.insertIntoBST(root.right,val)
root.right = right
return root
思路卡在了当所要删除的节点的左右子树均不为空时,如何操作?
第一遍思考,思路方向是旋转左右子树,但这是不对的。
左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
递归法(版本一)
class Solution:
def deleteNode(self, root, key):
if root is None:
return root
if root.val == key:
if root.left is None and root.right is None:
return None
elif root.left is None:
return root.right
elif root.right is None:
return root.left
else:
cur = root.right
while cur.left is not None:
cur = cur.left
cur.left = root.left
return 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
递归法(版本二)
class Solution:
def deleteNode(self, root, key):
if root is None: # 如果根节点为空,直接返回
return root
if root.val == key: # 找到要删除的节点
if root.right is None: # 如果右子树为空,直接返回左子树作为新的根节点
return root.left
cur = root.right
while cur.left: # 找到右子树中的最左节点
cur = cur.left
root.val, cur.val = cur.val, root.val # 将要删除的节点值与最左节点值交换
root.left = self.deleteNode(root.left, key) # 在左子树中递归删除目标节点
root.right = self.deleteNode(root.right, key) # 在右子树中递归删除目标节点
return root
迭代法
class Solution:
def deleteOneNode(self, target: TreeNode) -> TreeNode:
"""
将目标节点(删除节点)的左子树放到目标节点的右子树的最左面节点的左孩子位置上
并返回目标节点右孩子为新的根节点
是动画里模拟的过程
"""
if target is None:
return target
if target.right is None:
return target.left
cur = target.right
while cur.left:
cur = cur.left
cur.left = target.left
return target.right
def deleteNode(self, root: TreeNode, key: int) -> TreeNode:
if root is None:
return root
cur = root
pre = None # 记录cur的父节点,用来删除cur
while cur:
if cur.val == key:
break
pre = cur
if cur.val > key:
cur = cur.left
else:
cur = cur.right
if pre is None: # 如果搜索树只有头结点
return self.deleteOneNode(cur)
# pre 要知道是删左孩子还是右孩子
if pre.left and pre.left.val == key:
pre.left = self.deleteOneNode(cur)
if pre.right and pre.right.val == key:
pre.right = self.deleteOneNode(cur)
return root
思路上,意识到了,要找到所要删除节点的右子树的最左节点,然后将要删除节点的左子树嫁接上去。
但是认为需要一个parent全局变量来保存当前遍历节点的右节点,但是其实是不必的。
只需要一个root,返回也只返回删除root后的根节点就好。
判断条件是学习的关键。
class Solution:
def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
if root == None :
return None
if root.val == key :
if root.left == None and root.right == None :
return None
elif root.left == None and root.right != None :
return root.right
elif root.left != None and root.right == None :
return root.left
else :
res = root.right
while res.left :
res = res.left
res.left = root.left
return 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
这里的右子树返回给父节点的左孩子的思想,在第一次看题时想到了一点。
该题中存在一种很容易陷入误区的错误写法,注意即可。
递归法(版本一)
class Solution:
def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
if root is None:
return None
if root.val < low:
# 寻找符合区间 [low, high] 的节点
return self.trimBST(root.right, low, high)
if root.val > high:
# 寻找符合区间 [low, high] 的节点
return self.trimBST(root.left, low, high)
root.left = self.trimBST(root.left, low, high) # root.left 接入符合条件的左孩子
root.right = self.trimBST(root.right, low, high) # root.right 接入符合条件的右孩子
return root
递归法(版本二)精简
class Solution:
def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
if root is None:
return None
if root.val < low:
return self.trimBST(root.right, low, high)
if root.val > high:
return self.trimBST(root.left, low, high)
root.left = self.trimBST(root.left, low, high)
root.right = self.trimBST(root.right, low, high)
return root
迭代法
class Solution:
def trimBST(self, root: TreeNode, L: int, R: int) -> TreeNode:
if not root:
return None
# 处理头结点,让root移动到[L, R] 范围内,注意是左闭右闭
while root and (root.val < L or root.val > R):
if root.val < L:
root = root.right # 小于L往右走
else:
root = root.left # 大于R往左走
cur = root
# 此时root已经在[L, R] 范围内,处理左孩子元素小于L的情况
while cur:
while cur.left and cur.left.val < L:
cur.left = cur.left.right
cur = cur.left
cur = root
# 此时root已经在[L, R] 范围内,处理右孩子大于R的情况
while cur:
while cur.right and cur.right.val > R:
cur.right = cur.right.left
cur = cur.right
return root
这题的代码逻辑还是要好好理顺的,看着简单,但是前面两个 if 的判断,如果root是要删除的节点,如果 root 的值小,返回了当前 root 的右子树,而在后面递归逻辑中,root 的父节点的左孩子,会接住 root 的右子树,而如果 root 是小值,那么root的左孩子所有也和 root 一起被删除了。
class Solution:
def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]:
if root is None:
return None
if root.val < low:
return self.trimBST(root.right, low, high)
if root.val > high:
return self.trimBST(root.left, low, high)
root.left = self.trimBST(root.left, low, high)
root.right = self.trimBST(root.right, low, high)
return root
找中间节点,然后拆分区间就可以了吧。
这是第一次看题想到的思路。
递归法
class Solution:
def traversal(self, nums: List[int], left: int, right: int) -> TreeNode:
if left > right:
return None
mid = left + (right - left) // 2
root = TreeNode(nums[mid])
root.left = self.traversal(nums, left, mid - 1)
root.right = self.traversal(nums, mid + 1, right)
return root
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
root = self.traversal(nums, 0, len(nums) - 1)
return root
迭代法
from collections import deque
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
if len(nums) == 0:
return None
root = TreeNode(0) # 初始根节点
nodeQue = deque() # 放遍历的节点
leftQue = deque() # 保存左区间下标
rightQue = deque() # 保存右区间下标
nodeQue.append(root) # 根节点入队列
leftQue.append(0) # 0为左区间下标初始位置
rightQue.append(len(nums) - 1) # len(nums) - 1为右区间下标初始位置
while nodeQue:
curNode = nodeQue.popleft()
left = leftQue.popleft()
right = rightQue.popleft()
mid = left + (right - left) // 2
curNode.val = nums[mid] # 将mid对应的元素给中间节点
if left <= mid - 1: # 处理左区间
curNode.left = TreeNode(0)
nodeQue.append(curNode.left)
leftQue.append(left)
rightQue.append(mid - 1)
if right >= mid + 1: # 处理右区间
curNode.right = TreeNode(0)
nodeQue.append(curNode.right)
leftQue.append(mid + 1)
rightQue.append(right)
return root
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
if nums == [] :
return None
n = len(nums)
idx = n//2
middle = nums[idx]
mroot = TreeNode(middle)
left = nums[:idx]
right = nums[idx+1:n]
lnode = self.sortedArrayToBST(left)
rnode = self.sortedArrayToBST(right)
mroot.left = lnode
mroot.right = rnode
return mroot
何为,累加树?
不明白概念,所以第一次审题,并无思路。
没仔细审题!累加树不是一个标准定义,是题目中自己定义的:使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
换成数组中,就是一个倒序遍历,然后一直累加!
既然如此,逻辑就是反中序遍历。同样要搞一个 pre 全局变量,记录一直以来的累计值。
想明白这个逻辑之后,递归很简单。
递归法(版本一)
class Solution:
def convertBST(self, root: TreeNode) -> TreeNode:
self.pre = 0 # 记录前一个节点的数值
self.traversal(root)
return root
def traversal(self, cur):
if cur is None:
return
self.traversal(cur.right)
cur.val += self.pre
self.pre = cur.val
self.traversal(cur.left)
递归法(版本二)
class Solution:
def __init__(self):
self.count = 0
def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root == None:
return
'''
倒序累加替换:
'''
# 右
self.convertBST(root.right)
# 中
# 中节点:用当前root的值加上pre的值
self.count += root.val
root.val = self.count
# 左
self.convertBST(root.left)
return root
迭代法(版本一)
class Solution:
def __init__(self):
self.pre = 0 # 记录前一个节点的数值
def traversal(self, root):
stack = []
cur = root
while cur or stack:
if cur:
stack.append(cur)
cur = cur.right # 右
else:
cur = stack.pop() # 中
cur.val += self.pre
self.pre = cur.val
cur = cur.left # 左
def convertBST(self, root):
self.pre = 0
self.traversal(root)
return root
迭代法(版本二)
class Solution:
def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if not root: return root
stack = []
result = []
cur = root
pre = 0
while cur or stack:
if cur:
stack.append(cur)
cur = cur.right
else:
cur = stack.pop()
cur.val+= pre
pre = cur.val
cur =cur.left
return root
class Solution:
def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
self.pre = 0
return self.digui(root)
def digui(self,root):
if root == None :
return
self.digui(root.right)
root.val += self.pre
self.pre = root.val
self.digui(root.left)
return root
牛逼!代码随想录这篇总结写的真好,N刷时,就对着这一篇刷就没问题。
二叉树总结篇