相关推荐
python coding with ChatGPT 打卡第12天| 二叉树:理论基础
python coding with ChatGPT 打卡第13天| 二叉树的深度优先遍历
python coding with ChatGPT 打卡第14天| 二叉树的广度优先遍历
python coding with ChatGPT 打卡第15天| 二叉树:翻转二叉树、对称二叉树
python coding with ChatGPT 打卡第16天| 二叉树:完全二叉树、平衡二叉树、二叉树的所有路径、左叶子之和
python coding with ChatGPT 打卡第17天| 二叉树:找树左下角的值、路径总和
python coding with ChatGPT 打卡第18天| 二叉树:从中序与后序遍历序列构造二叉树、最大二叉树
python coding with ChatGPT 打卡第19天| 二叉树:合并二叉树
python coding with ChatGPT 打卡第20天| 二叉搜索树:搜索、验证、最小绝对差、众数
python coding with ChatGPT 打卡第21天| 二叉树:最近公共祖先
文章目录
- 二叉搜索树的插入操作
- Key Points
- 相关题目
- 视频讲解
- 重点分析
- 删除二叉搜索树的节点
- Key Points
- 相关题目
- 视频讲解
- 重点分析
- 修剪二叉搜索树
- Key Points
- 相关题目
- 视频讲解
- 重点分析
- 将有序数组转换为二叉搜索树
- Key Points
- 相关题目
- 视频讲解
- 重点分析
- 把二叉搜索树转换为累加树
- Key Points
- 相关题目
- 视频讲解
- 重点分析
二叉搜索树的插入操作
Key Points
BST的性质:对于每个节点,其左子树只包含小于当前节点的数,其右子树只包含大于当前节点的数。
相关题目
701. 二叉搜索树的插入操作
视频讲解
二叉搜索树的插入操作
重点分析
方法一:递归法
def insertIntoBST(root, val):# 如果当前节点为空,创建一个新节点并返回if not root:return TreeNode(val)# 如果新值小于当前节点的值,递归地插入到左子树if val < root.val:root.left = insertIntoBST(root.left, val)# 如果新值大于当前节点的值,递归地插入到右子树else:root.right = insertIntoBST(root.right, val)# 返回根节点return root
方法二:迭代法
def insertIntoBST(root, val):if not root:return TreeNode(val)current = rootwhile True:if val < current.val:if current.left:current = current.leftelse:current.left = TreeNode(val)break # 插入完成,退出循环else: # val > current.valif current.right:current = current.rightelse:current.right = TreeNode(val)break # 插入完成,退出循环return root
删除二叉搜索树的节点
Key Points
分类讨论:
- 没找到节点
- 叶子节点
- 只有一个孩子的节点
- 有两个孩子的节点
相关题目
450. 删除二叉搜索树种的节点
视频讲解
调整二叉树的结构最难
重点分析
我们需要从根节点开始,使用二叉搜索树的性质来定位要删除的节点。这意味着:
如果 key 小于当前节点的值,则我们应该在左子树中查找 key。
如果 key 大于当前节点的值,则我们应该在右子树中查找 key。
如果 key 等于当前节点的值,那么我们就找到了要删除的节点。
例如:删除如下二叉树值为20的节点
其中,删除有两个孩子的节点是最重要的,主要有两种思路:
- 用删除节点的左孩子替换删除节点,把删除节点的右孩子整个放在左孩子的最大节点的右侧。
- 优点:不用递归调用删除节点的函数
- 缺点:新二叉树结构改变会很大(如:平衡->不平衡)
- 用删除节点的左孩子的最大值替换删除节点,然后递归删除左孩子的最大节点。
- 优点:新二叉树结构变化较小
- 缺点:需要递归调用
方法一:
方法二:
方法一:
递归法:
def get_max_child(node):while node.right:node = node.rightreturn nodedef deleteNode(root, key):if not root:return rootif root.val > key:root.left = deleteNode(root.left, key)return root # 返回root才完整if root.val < key:root.right = deleteNode(root.right, key)return rootelse:if not root.left and not root.right:return Noneif not root.left:return root.rightif not root.right:return root.leftnode = get_max_child(root.left)node.right = root.rightreturn root.left
写递归的逻辑:
某个情形的逻辑要完整(return)
方法一:
迭代法
def deleteNode(root, key):current = rootparent = Nonewhile current:if current.val > key:parent = currentcurrent = current.leftelif current.val < key:parent = currentcurrent = current.rightelse:if not current.left and not current.right:if not parent:return Noneelse:if parent.left == current:parent.left = Noneelse:parent.right = Nonereturn rootelif not current.right:if not parent:return current.leftelse:if parent.left == current:parent.left = current.leftelse:parent.right = current.leftreturn rootelif not current.left:if not parent:return current.rightelse:if parent.left == current:parent.left = current.rightelse:parent.right = current.rightreturn rootelse:node = get_max_child(current.left)node.right = current.rightif not parent:return current.leftelse:if parent.left == current:parent.left = current.leftelse:parent.right = current.leftreturn rootreturn root
迭代法注意考虑parent为None的情况
方法二:
递归法
def deleteNode(root, key):if not root:return rootif root.val > key:root.left = deleteNode(root.left, key)return rootif root.val < key:root.right = deleteNode(root.right, key)return rootelse:if not root.left and not root.right:return Noneif not root.left:return root.rightif not root.right:return root.leftnode = get_max_child(root.left)root.val = node.valroot.left = deleteNode(root.left, node.val)return root
方法二:
迭代法
current = rootparent = Nonewhile current:if current.val > key:parent = currentcurrent = current.leftelif current.val < key:parent = currentcurrent = current.rightelse:if not current.left and not current.right:if not parent:return Noneelse:if parent.left == current:parent.left = Noneelse:parent.right = Nonereturn rootelif not current.right:if not parent:return current.leftelse:if parent.left == current:parent.left = current.leftelse:parent.right = current.leftreturn rootelif not current.left:if not parent:return current.rightelse:if parent.left == current:parent.left = current.rightelse:parent.right = current.rightreturn rootelse:node = get_max_child(current.left)current.val = node.valcurrent.left = deleteNode(current.left, node.val)return rootreturn root
修剪二叉搜索树
Key Points
易错点:
3/ \0 4\2/1
[1,3]
相关题目
669. 修剪二叉搜索树
视频讲解
如何修剪二叉树
重点分析
方法一: 递归法
def trimBST(root, low, high):if not root:return None# 如果节点值小于low,那么它的左子树都不在范围内,应修剪掉左子树if root.val < low:root = trimBST(root.right, low, high)# 如果节点值大于high,那么它的右子树都不在范围内,应修剪掉右子树elif root.val > high:root = trimBST(root.left, low, high)# 如果节点值在[low, high]范围内,递归修剪其左右子树else:root.left = trimBST(root.left, low, high)root.right = trimBST(root.right, low, high)return root
方法二:迭代法
def trimBST(root, low, high):dummy = TreeNode(float("inf")) # 创建虚拟父节点dummy.left = root # 将原树的根节点作为虚拟父节点的左子节点stack = [(dummy, root, True)] # 初始化栈,包含虚拟父节点、根节点、根节点是左子节点的信息while stack:parent, node, isLeft = stack.pop() # 弹出当前节点及其父节点信息if not node:continue# 如果当前节点值小于low,则需要修剪当前节点及其左子树if node.val < low:# 根据是左子节点还是右子节点,更新父节点的链接if isLeft:parent.left = node.right # 将右子节点(可能在范围内)连接到父节点else:parent.right = node.rightstack.append((parent, node.right, isLeft)) # 继续检查右子节点# 如果当前节点值大于high,逻辑相同,但修剪右子树elif node.val > high:if isLeft:parent.left = node.leftelse:parent.right = node.leftstack.append((parent, node.left, isLeft))# 如果当前节点值在[low, high]范围内,保留节点,继续检查其子节点else:stack.append((node, node.left, True))stack.append((node, node.right, False))return dummy.left # 返回修剪后的新树的根节点
这段代码使用了一个栈来追踪遍历过程中的节点以及它们的父节点和是否为左子节点的信息。对每个节点,我们检查它是否需要被修剪(即它的值是否在 [low, high] 范围之外)。如果是,则根据它是左子节点还是右子节点,更新父节点的相应链接。如果节点的值在范围内,我们将继续处理它的子节点。
请注意,这个方法虽然避免了递归,但逻辑复杂度增加了,特别是在管理父节点和子节点关系时。对于二叉树的修改操作,递归方法通常更为直观和易于理解。
将有序数组转换为二叉搜索树
Key Points
分而治之的策略:选取中间元素作为根节点,然后对左右子数组分别执行同样的操作
相关题目
108将有序数组转换为二叉搜索树
视频讲解
构造平衡二叉搜索树
重点分析
关键在于每次都要选取数组中间的元素作为树的根节点,这样可以确保树的左右两边保持平衡。
方法一:递归法
def sortedArrayToBST(nums):if not nums:return Nonemid = len(nums) // 2root_val = nums[mid]root = TreeNode(root_val)root.left = sortedArrayToBST(nums[:mid])root.right = sortedArrayToBST(nums[mid+1:])return root
方法二:迭代法
def sortedArrayToBST(nums):if not nums:return None# 用于保存处理节点的栈node_stack = []# 用于保存数组范围的栈range_stack = [(0, len(nums) - 1)]# 创建一个虚拟的根节点root = TreeNode(0)node_stack.append((root, True))while range_stack:left, right = range_stack.pop()parent, is_left = node_stack.pop()if left > right:continuemid = (left + right) // 2# 创建当前节点current_node = TreeNode(nums[mid])# 将当前节点连接到父节点if is_left:parent.left = current_nodeelse:parent.right = current_node# 处理左子树node_stack.append((current_node, True))range_stack.append((left, mid - 1))# 处理右子树node_stack.append((current_node, False))range_stack.append((mid + 1, right))return root.left
把二叉搜索树转换为累加树
Key Points
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
相关题目
538. 把二叉搜索树转换为累加树
视频讲解
二叉树完结
重点分析
方法一:递归法
class Solution(object):def __init__(self):self.sum_value = 0def convertBST(self, root):""":type root: TreeNode:rtype: TreeNode"""if not root:return rootroot.right = self.convertBST(root.right)self.sum_value += root.valroot.val = self.sum_valueroot.left = self.convertBST(root.left)return root
方法二:迭代法 反向中序遍历
按照反向中序遍历(右->节点->左)的顺序,使用栈来迭代地遍历树
def convertBST(root):if not root:return rootcurrent = rootstack = []sum_value = 0while current or stack:# 将当前节点及其所有右子节点压入栈while current:stack.append(current)current = current.rightcurrent = stack.pop() # 访问栈顶元素(当前最大节点)sum_value += current.valcurrent.val = sum_valuecurrent = current.left # 转向左子树return root
方法二:迭代法 统一迭代法(颜色标记)
详见 python coding with ChatGPT 打卡第13天| 二叉树的深度优先遍历 中的迭代遍历 拓展 统一的迭代法
def convertBST(root):if not root:return rootWHITE = 0 # 使用常量来提高代码可读性GREY = 1stack = [(root, WHITE)]sum_value = 0 # 累加变量,用于更新节点的值while stack:node, color = stack.pop()if color == WHITE: # 节点首次访问,先处理右子节点stack.append((node, GREY)) if node.right:stack.append((node.right, WHITE))else: # 节点第二次访问,更新节点值并处理左子节点sum_value += node.valnode.val = sum_valueif node.left:stack.append((node.left, WHITE))return root
GPT4点评: