文章目录
前言
今天带大家进行二叉树的实战篇3,学会并了解如果去修改构造一棵二叉树,并且将深度解析我们叉树数中经常用到的搜索二叉树,无论什么要二叉树结构修改,搜索二叉树的性质等等,一文带大家弄懂。本文用于记录自己的学习过程,同时向大家进行分享相关的内容。本文内容参考于代码随想录同时包含了自己的许多学习思考过程,如果有错误的地方欢迎批评指正!
二叉树的修改与构造
翻转二叉树
相关技巧:首先来看题意哈,我们需要翻转二叉树,翻转二叉树的方式是不是跟之前做过的是否对称是相同的道理,只是这里多一步交换节点的步骤,所以我们就是要关于root节点轴对称交换,左边的外面与右边的外面交换,左边的里面与右边的里面交换,就是这个意思。同样的我们用递归的方式来解。来看看递归三要素。
- 确定递归的参数和返回值:很明显看这道题,我们需要做什么,是交换节点,所以我们这传入的参数肯定就是根节点即可,最后需要返回的就是二者的交换结果,所以返回值就是root
- 确定递归的终止条件:什么时候终止,这里来看,当节点为空直接返回即可。
- 确定单层递归的逻辑:在确定还没到达递归终止的条件的时候就是单层递归的逻辑了,处理的情况就是二者均不为空且值相等。怎么处理,我们直接交换节点的左右子树即可,然后递归的去交换左子树和右子树
# 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 invertTree(self, root: TreeNode) -> TreeNode:
if not root:
return None
root.left, root.right = root.right, root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
从中序与后序遍历序列构造二叉树
106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
相关技巧:我们是可以通过前序和中序遍历或者中序和后序遍历来确定并构建唯一的二叉树的,但是要记住,只有前序和后序顺序的时候是不能够构建出唯一的二叉树的哦。我们这里以中序和后序来看,我们首先知道中序的顺序是左根右,后序的顺序是左右根,那么从这顺序就可看出了,当不为空时我们后序的最后一个节点一定是根节点,那么我们可以通过后序找出根节点,在在中序中根据找到的根节点划分出左子树和右子树,找出来之后,我们再去后序中找到左子树和右子树,这就通过中序中的左子树节点个数直接来划分就可以了,因为左子树的节点个数一定是一样的。同理,我们在从后序顺序划分出的左子树同样之前的处理,然后在右子树处理,递归处理即可。
- 确定递归的参数和返回值:传入的参数就是构建树的区间,返回值就是节点
- 确定递归的终止条件:当区间长度为1,说明只有这个节点了,就构建节点进行返回
- 确定单层递归的逻辑:首先根据后序找根节点,在中序划分出左右子树,在去后序同样的划分,继续递归处理区间
# 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 buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
# 第一步: 特殊情况讨论: 树为空. (递归终止条件)
if not postorder:
return None
# 第二步: 后序遍历的最后一个就是当前的中间节点.
root_val = postorder[-1]
root = TreeNode(root_val)
# 第三步: 找切割点.
separator_idx = inorder.index(root_val)
# 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
inorder_left = inorder[:separator_idx]
inorder_right = inorder[separator_idx + 1:]
# 第五步: 切割postorder数组. 得到postorder数组的左,右半边.
# ⭐️ 重点1: 中序数组大小一定跟后序数组大小是相同的.
postorder_left = postorder[:len(inorder_left)]
postorder_right = postorder[len(inorder_left): len(postorder) - 1]
# 第六步: 递归
root.left = self.buildTree(inorder_left, postorder_left)
root.right = self.buildTree(inorder_right, postorder_right)
# 第七步: 返回答案
return root
最大二叉树
相关技巧:其实跟上题构建二叉树的思想有些类似,这里就是直接告诉你根节点怎么来的,不需要你在去通过后序的原理去找根节点了。
- 确定递归的参数和返回值:与刚才一样传入的参数就是构建树的区间,返回值就是节点
- 确定递归的终止条件:当区间长度为1,说明只有这个节点了,就构建节点进行返回
- 确定单层递归的逻辑:根据最大值,找出根节点的位置,在划分出左右子树,继续递归处理区间
# 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 constructMaximumBinaryTree(self, nums: List[int]) -> TreeNode:
if len(nums) == 1:
return TreeNode(nums[0])
node = TreeNode(0)
# 找到数组中最大的值和对应的下标
maxValue = 0
maxValueIndex = 0
for i in range(len(nums)):
if nums[i] > maxValue:
maxValue = nums[i]
maxValueIndex = i
node.val = maxValue
# 最大值所在的下标左区间 构造左子树
if maxValueIndex > 0:
new_list = nums[:maxValueIndex]
node.left = self.constructMaximumBinaryTree(new_list)
# 最大值所在的下标右区间 构造右子树
if maxValueIndex < len(nums) - 1:
new_list = nums[maxValueIndex+1:]
node.right = self.constructMaximumBinaryTree(new_list)
return node
合并二叉树
相关技巧:这个题目还是比较简单的,两个数相同的位置的值相加即可,当某个树为空时直接返回另一个树的节点即可。
- 确定递归的参数和返回值:传入的就是需要处理的两个二叉树,返回值即为最终的树
- 确定递归的终止条件:当tree1空时,返回tree2,当tree2空时返回tree1
- 确定单层递归的逻辑:两者的值相加即可,然后递归处理tree1左tree2左,tree1右tree2右
# 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 mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
# 递归终止条件:
# 但凡有一个节点为空, 就立刻返回另外一个. 如果另外一个也为None就直接返回None.
if not root1:
return root2
if not root2:
return root1
# 上面的递归终止条件保证了代码执行到这里root1, root2都非空.
root1.val += root2.val # 中
root1.left = self.mergeTrees(root1.left, root2.left) #左
root1.right = self.mergeTrees(root1.right, root2.right) # 右
return root1
二叉搜索树的属性
二叉搜索树中的搜索
相关技巧:寻找值相等的节点,首先我们要明确二叉搜索树的性质,小于根节点的在左子树,大于根节点的在右子树,其左右子树也均为二叉搜索树,那么我们就能很快的解出来了。
- 确定递归的参数和返回值:传入的就是根节点,返回值即以该节点为根节点的子树
- 确定递归的终止条件:当节点为空时即不存在这个节点或者当值相等即找到了节点,直接返回root
- 确定单层递归的逻辑:我们比较root与val的值,大于就进左子树继续搜索,小于就进右子树继续搜索
# 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 searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
# 为什么要有返回值:
# 因为搜索到目标节点就要立即return,
# 这样才是找到节点就返回(搜索某一条边),如果不加return,就是遍历整棵树了。
if not root or root.val == val:
return root
if root.val > val:
return self.searchBST(root.left, val)
if root.val < val:
return self.searchBST(root.right, val)
验证二叉搜索树
相关技巧:这道题乍一看很简单,我们只需要左子树小于根节点,右子树大于根节点,然后递归的看看是不是都满足即可。但是这样是会出问题的,举个例子就是根节点的右孩子是大于根节点的,那么其右孩子的左孩子就要小于右孩子,但是这个又需要大于根节点的,所以刚才那个简单的判断是不能判断这个情况的,那么我们怎么去处理呢,记住二叉搜索树的一个最重要的性质,其中序遍历为单调递增的。所以我们直接中序遍历后看是否满足单调递增即可。
- 确定递归的参数和返回值:递归的就是单调递增的情况,参数为根节点,返回值为最终的遍历结果
- 确定递归的终止条件:节点为空返回none
- 确定单层递归的逻辑:先进左在输出在进右,即左根右
# 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 __init__(self):
self.vec = []
def traversal(self, root):
if root is None:
return
self.traversal(root.left)
self.vec.append(root.val) # 将二叉搜索树转换为有序数组
self.traversal(root.right)
def isValidBST(self, root):
self.vec = [] # 清空数组
self.traversal(root)
for i in range(1, len(self.vec)):
# 注意要小于等于,搜索树里不能有相同元素
if self.vec[i] <= self.vec[i - 1]:
return False
return True
二叉搜索树的最小绝对差
530. 二叉搜索树的最小绝对差 - 力扣(LeetCode)
相关技巧:这题同样的利用到了二叉搜索树的中序遍历顺序递增性质,差值最小的话,我们排完序之后,其差值最小一定会出现在相邻的两个元素之间的。然后通过排序的结果找出最小差值
- 确定递归的参数和返回值:递归的就是单调递增的情况,参数为根节点,返回值为最终的遍历结果
- 确定递归的终止条件:节点为空返回none
- 确定单层递归的逻辑:先进左在输出在进右,即左根右
# 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 __init__(self):
self.vec = []
def traversal(self, root):
if root is None:
return
self.traversal(root.left)
self.vec.append(root.val) # 将二叉搜索树转换为有序数组
self.traversal(root.right)
def getMinimumDifference(self, root):
self.vec = []
self.traversal(root)
if len(self.vec) < 2:
return 0
result = float('inf')
for i in range(1, len(self.vec)):
# 统计有序数组的最小差值
result = min(result, self.vec[i] - self.vec[i - 1])
return result
二叉搜索树中的众数
相关技巧:看题目找众数,怎么处理同样的我们还是中序遍历,从小到大的顺序,每次记录如果相同就加1并比较是否大于最多次的情况,大于就更新max
- 确定递归的参数和返回值:递归的就是单调递增的情况,参数为根节点,返回值为最终的遍历结果
- 确定递归的终止条件:节点为空返回none
- 确定单层递归的逻辑:先进左在输出在进右,即左根右,然后处理就是比较当前节点与上个节点是否相同,相同的话计数count+1,并比较与maxcount的,大于就更新,同时结果更新结果result。
# 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 __init__(self):
self.maxCount = 0 # 最大频率
self.count = 0 # 统计频率
self.pre = None
self.result = []
def searchBST(self, cur):
if cur is None:
return
self.searchBST(cur.left) # 左
# 中
if self.pre is None: # 第一个节点
self.count = 1
elif self.pre.val == cur.val: # 与前一个节点数值相同
self.count += 1
else: # 与前一个节点数值不同
self.count = 1
self.pre = cur # 更新上一个节点
if self.count == self.maxCount: # 如果与最大值频率相同,放进result中
self.result.append(cur.val)
if self.count > self.maxCount: # 如果计数大于最大值频率
self.maxCount = self.count # 更新最大频率
self.result = [cur.val] # 很关键的一步,不要忘记清空result,之前result里的元素都失效了
self.searchBST(cur.right) # 右
return
def findMode(self, root):
self.count = 0
self.maxCount = 0
self.pre = None # 记录前一个节点
self.result = []
self.searchBST(root)
return self.result
把二叉搜索树转换为累加树
538. 把二叉搜索树转换为累加树 - 力扣(LeetCode)
相关技巧:一看到这题大家可能有点懵,这个怎么累加,怎么处理的,岂不是会很麻烦。**其实这就是一棵树,大家可能看起来有点别扭,换一个角度来看,这就是一个有序数组[2, 5, 13],求从后到前的累加数组,也就是[20, 18, 13],是不是感觉这就简单了。**怎么累加呢?累积的顺序是右中左,其实我们倒中序遍历就可以了,然后进行累加即可。
- 确定递归的参数和返回值:递归的就是单调递增的情况,参数为根节点,返回值为最终的遍历结果
- 确定递归的终止条件:节点为空返回none
- 确定单层递归的逻辑:本题目就是先进右在进左,即右根左,然后处理就是比较当前节点与之前的结果进行累加并保存
# 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):
if cur is None:
return
self.traversal(cur.right)
cur.val += self.pre
self.pre = cur.val
self.traversal(cur.left)
算法基础系列
一文了解什么是数组及其经典考察题目
走进链表及其经典考察题目
还不知道什么是哈希表,看这篇文章就够了
字符串匹配究极大招【KMP】:带你一步步从原理到构建
【栈与队列】:基础实战篇-CSDN博客
【双指针法】:这么常用的你怎么能不知道-CSDN博客
【二叉树】理论基础篇1-CSDN博客
【二叉树】实战篇1-CSDN博客
【二叉树】实战篇2-CSDN博客