代码随想录训练营 Day21打卡 二叉树 part08
一、 力扣669. 修剪二叉搜索树
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。
所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
示例 :
输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]
版本一 递归法
实现思路
- 首先检查当前节点是否为空,如果为空则返回空。
- 如果当前节点的值小于区间的下界,则修剪右子树,因为左子树的所有节点都小于当前节点,不可能符合条件。
- 如果当前节点的值大于区间的上界,则修剪左子树,因为右子树的所有节点都大于当前节点,不可能符合条件。
- 如果当前节点的值在区间范围内,则递归地修剪左右子树。
- 返回修剪后的当前节点。
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def trimBST(self, root: TreeNode, low: int, high: int) -> TreeNode:
# 如果当前节点为空,直接返回空
if root is None:
return None
# 如果当前节点的值小于区间的下界low,那么整个左子树都不符合条件
# 继续修剪右子树,寻找符合条件的节点
if root.val < low:
return self.trimBST(root.right, low, high)
# 如果当前节点的值大于区间的上界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
版本二 迭代法
实现思路
-
检查根节点是否为空,如果为空则返回空。
-
处理根节点,使其移动到区间 [L, R] 范围内。
如果根节点的值小于L,则根节点需要移动到右子树,因为左子树所有节点都小于当前节点。
如果根节点的值大于R,则根节点需要移动到左子树,因为右子树所有节点都大于当前节点。 -
处理左子树中的节点,使所有左子树的节点值都大于等于L。
遍历左子树,如果左孩子节点的值小于L,则删除该左孩子节点,并让左孩子节点指向其右孩子节点。
-
处理右子树中的节点,使所有右子树的节点值都小于等于R。
遍历右子树,如果右孩子节点的值大于R,则删除该右孩子节点,并让右孩子节点指向其左孩子节点。
-
返回处理后的根节点。
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
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
力扣题目链接
题目文章讲解
题目视频讲解
二、 力扣108. 将有序数组转换为二叉搜索树
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 平衡 二叉搜索树。
示例 :
输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
分割点就是数组中间位置的节点。
那么为问题来了,如果数组长度为偶数,中间节点有两个,取哪一个?
取哪一个都可以,只不过构成了不同的平衡二叉搜索树。
例如:输入:[-10,-3,0,5,9]
如下两棵树,都是这个数组的平衡二叉搜索树:
如果要分割的数组长度为偶数的时候,中间元素为两个,是取左边元素 就是树1,取右边元素就是树2。
这也是题目中强调答案不是唯一的原因。 理解这一点,这道题目算是理解到位了。
版本一 递归法
实现思路
- 定义一个辅助函数 traversal 用于递归构建树。
- 在 traversal 中,判断递归的终止条件,即 left 超过 right 时返回 None。
- 计算当前数组的中间元素位置 mid,并以该元素为根节点。
- 递归地构建左子树和右子树。
- 在 sortedArrayToBST 中调用 traversal 函数并返回构建的树的根节点。
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
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])
# 递归构建左子树,范围是从left到mid-1
root.left = self.traversal(nums, left, mid - 1)
# 递归构建右子树,范围是从mid+1到right
root.right = self.traversal(nums, mid + 1, right)
# 返回当前根节点
return root
def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
# 从nums数组中创建高度平衡的二叉搜索树
return self.traversal(nums, 0, len(nums) - 1)
版本二 递归法 精简
实现思路
- 判断数组是否为空,如果为空则返回 None。
- 计算数组的中间元素位置 mid,并以该元素为根节点。
- 递归地构建左子树和右子树。
- 左子树使用数组的前半部分(不包括中间元素),右子树使用数组的后半部分(不包括中间元素)。
- 返回构建的树的根节点。
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
# 如果数组为空,返回空
if not nums:
return None
# 计算中间位置
mid = len(nums) // 2
# 以中间元素为根节点
root = TreeNode(nums[mid])
# 递归构建左子树
root.left = self.sortedArrayToBST(nums[:mid])
# 递归构建右子树
root.right = self.sortedArrayToBST(nums[mid + 1:])
# 返回当前根节点
return root
版本三 迭代法
实现思路
-
判断数组是否为空,如果为空则返回 None。
-
创建根节点,并初始化队列用于存放节点及其对应的左右区间。
-
将初始根节点及其左右区间加入队列。
-
使用循环处理队列中的每个节点:
取出当前节点和其对应的左右区间。
计算中间位置,并设置当前节点的值为中间元素。
如果左区间存在,创建左孩子节点并将其加入队列。
如果右区间存在,创建右孩子节点并将其加入队列。 -
返回构建好的树的根节点。
from collections import deque
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
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)
rightQue.append(len(nums) - 1)
# 处理队列中的每个节点
while nodeQue:
# 取出当前节点和其对应的左右区间
curNode = nodeQue.popleft()
left = leftQue.popleft()
right = rightQue.popleft()
# 计算中间位置
mid = left + (right - left) // 2
# 设置当前节点的值为中间元素
curNode.val = nums[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
力扣题目链接
题目文章讲解
题目视频讲解
三、 力扣538. 把二叉搜索树转换为累加树
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
提醒一下,二叉搜索树满足下列约束条件:
节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。
示例 :
输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]
从树中可以看出累加的顺序是右中左,所以我们需要反中序遍历这个二叉树,然后顺序累加就可以了。
遍历顺序如图所示:
本题依然需要一个pre指针记录当前遍历节点cur的前一个节点,这样才方便做累加。
版本一 递归法
实现思路
- 定义一个辅助变量 pre 来记录前一个节点的累加和。
- 定义一个辅助函数 traversal 用于递归遍历树。
- 采用反中序遍历(右-中-左)来遍历树,先处理右子树,再处理当前节点,最后处理左子树。
- 在每个节点,更新其值为当前值加上 pre,然后更新 pre 为当前节点的新值。
- 在 convertBST 中初始化 pre 并调用 traversal 函数,返回处理后的根节点。
# Definition for a binary tree node.
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def convertBST(self, root: TreeNode) -> TreeNode:
self.pre = 0 # 记录前一个节点的累加和
self.traversal(root)
return root
def traversal(self, cur: TreeNode):
if cur is None:
return
# 先遍历右子树
self.traversal(cur.right)
# 更新当前节点的值
cur.val += self.pre
# 更新累加和
self.pre = cur.val
# 再遍历左子树
self.traversal(cur.left)
版本二 迭代法
实现思路
- 初始化一个辅助变量 pre 来记录前一个节点的累加和。
- 定义一个辅助函数 traversal 用于迭代遍历树。
- 使用栈进行反中序遍历(右-中-左)。
- 在每个节点,更新其值为当前值加上 pre,然后更新 pre 为当前节点的新值。
- 在 convertBST 中初始化 pre 并调用 traversal 函数,返回处理后的根节点。
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def __init__(self):
self.pre = 0 # 记录前一个节点的累加和
def traversal(self, root: TreeNode):
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: TreeNode) -> TreeNode:
self.pre = 0 # 重置累加和
self.traversal(root)
return root
力扣题目链接
题目文章讲解
题目视频讲解