leetcode分类刷题:二叉树(三、与深度相关的递归)

编程入门 行业动态 更新时间:2024-10-08 22:47:17

leetcode分类刷题:二叉树(三、与深度相关的<a href=https://www.elefans.com/category/jswz/34/1771140.html style=递归)"/>

leetcode分类刷题:二叉树(三、与深度相关的递归)

1、与深度相关的递归问题不同于深度优先遍历的前、中、后序遍历,该类问题在递归调用时带有返回值,但它们的核心难点还是在于递归三要素中的提取重复的逻辑,缩小问题规模,即递归函数内部的操作
2、在做了“110. 平衡二叉树”之后,会发现本次总结的题型全部都是自底向顶的递归,这种的逻辑还是有点难想的

104. 二叉树的最大深度

思路1、层序遍历:一般 层序遍历的基础上,输出最大高度值
思路2、递归:重复逻辑:二叉树节点的最大深度 等于 左右子树的最大深度+1

'''
104. 二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例 1:输入:[3,9,20,null,null,15,7]输出:3
思路1、层序遍历:一般 层序遍历的基础上,输出最大高度值
思路2、递归:重复逻辑:二叉树节点的最大深度 等于 左右子树的最大深度+1
'''
class Solution:def maxDepth(self, root: Optional[TreeNode]) -> int:# 情况1、树为空if root == None:return 0# 情况2、树不为空result = 0que = deque()que.append(root)while len(que) > 0:size = len(que)for _ in range(size):node = que.popleft()if node.left != None:que.append(node.left)if node.right != None:que.append(node.right)result += 1return result# 重复逻辑:二叉树节点的最大深度 等于 左右子树的最大深度+1def maxDepthRecursive(self, root: Optional[TreeNode]) -> int:# 1、确定函数参数和返回值def maxDepth(node: Optional[TreeNode]) -> int:# 2、终止条件if node == None:return 0else:  # 3、单次递归的操作leftN = maxDepth(node.left)rightN = maxDepth(node.right)return max(leftN, rightN) + 1return maxDepth(root)

111. 二叉树的最小深度

思路1、层序遍历:一般 层序遍历的基础上,输出最小高度值(第一次遍历到叶子节点的时候)
思路2、递归:重复逻辑:二叉树节点的最小深度 等于 左右子树的最小深度+1,当左右子树不同时存在需要分类讨论

'''
111. 二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
示例 1:输入:root = [3,9,20,null,null,15,7]输出:2
思路1、层序遍历:一般 层序遍历的基础上,输出最小高度值(第一次遍历到叶子节点的时候)
思路2、递归:重复逻辑:二叉树节点的最小深度 等于 左右子树的最小深度+1,当左右子树不同时存在需要分类讨论
'''
class Solution:def minDepth(self, root: Optional[TreeNode]) -> int:# 情况1、树为空if root == None:return 0# 情况2、树不为空result = 0que = deque()que.append(root)while len(que) > 0:size = len(que)for _ in range(size):node = que.popleft()if node.left == None and node.right == None:return result + 1if node.left != None:que.append(node.left)if node.right != None:que.append(node.right)result += 1return result# 重复逻辑:二叉树节点的最小深度 等于 左右子树的最小深度+1,当左右子树不同时存在需要分类讨论def minDepthRecursive(self, root: Optional[TreeNode]) -> int:# 1、确定函数参数和返回值def minD(node: Optional[TreeNode]) -> int:# 2、终止条件if node == None:return 0else:  # 3、单次递归的操作leftN = minD(node.left)rightN = minD(node.right)if node.left == None and node.right != None:  # 当一个左子树为空,右不为空,最低高度取决于右子树return rightN + 1elif node.left != None and node.right == None:  # 当一个右子树为空,左不为空,最低高度取决于左子树return leftN + 1else:return min(leftN, rightN) + 1  # 包含了叶子节点及左右子树都不为空的两种情况return minD(root)

559. N 叉树的最大深度

思路1、层序遍历:一般 层序遍历的基础上,输出最大高度值
思路2、递归:重复逻辑:N叉树节点的最大深度 等于 所有子树的最大深度+1

from typing import List, Optional, Union
from collections import deque
'''
559. N 叉树的最大深度
给定一个 N 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。示例 1:输入:root = [1,null,3,2,4,null,5,6]输出:3
题眼:二叉树的层序遍历
思路1、层序遍历:一般 层序遍历的基础上,输出最大高度值
思路2、递归:重复逻辑:N叉树节点的最大深度 等于 所有子树的最大深度+1
'''
class Solution:def maxDepth(self, root: 'Node') -> int:  # 递归解法if root == None:  # 简单情况return 0else:  # 重复逻辑:节点的最大高度 等于所有孩子的最大高度+1depth = 0for child in root.children:depth = max(depth, self.maxDepth(child))return depth + 1def maxDepthIteration(self, root: Optional[Node]) -> int:# 情况1、树为空if root == None:return 0# 情况2、树不为空result = 0que = deque()que.append(root)while len(que) > 0:size = len(que)for _ in range(size):node = que.popleft()if node.children != None:for child in node.children:que.append(child)result += 1return result

222. 完全二叉树的节点个数

本题如果按照普通二叉树来处理,则它的递归解法和“104. 二叉树的最大深度”题目极为类似,连续练习几道递归解法解题之后,递归函数的内部实现和黑盒函数调用区分体会更佳明显了

'''
222. 完全二叉树的节点个数
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。
若最底层为第 h 层,则该层包含 1~2h个节点。
示例 1:输入:root = [1,2,3,4,5,6]输出:6
思路1、按照普通二叉树求解,“104. 二叉树的最大深度”的相似题目,重复逻辑:二叉树的节点个数等于左右子树的节点个数+1
思路2、利用完全二叉树的性质,判断每个二叉树是否为满二叉树,满二叉树的节点个数等于2^深度-1,不是满二叉树就按照普通二叉树进行处理
'''
class Solution:# 思路1、按照普通二叉树求解:迭代法-简单的层序遍历def countNodesIteration(self, root: Optional[TreeNode]) -> int:if root == None:return 0result = 0dq = deque()dq.append(root)while len(dq) > 0:node = dq.popleft()result += 1  # 在每次弹出节点后面记录节点个数if node.left != None:dq.append(node.left)if node.right != None:dq.append(node.right)return result# 思路1、按照普通二叉树求解:递归法,重复逻辑:二叉树的节点个数等于左右子树的节点个数+1def countNodes(self, root: Optional[TreeNode]) -> int:if root == None:  # 终止条件:简单情况return 0else:  # 重复逻辑leftN = self.countNodes(root.left)rightN = self.countNodes(root.right)return leftN + rightN + 1# 思路2、利用完全二叉树的性质:迭代法-简单的层序遍历def countNodesIteration2(self, root: Optional[TreeNode]) -> int:if root == None:return 0result = 0dq = deque()dq.append(root)while len(dq) > 0:node = dq.popleft()# 判断node是否是满二叉树:分别求解左右两侧的高度是否相等leftN, rightN = 0, 0node1, node2 = node, nodewhile node1 != None:leftN += 1node1 = node1.leftwhile node2 != None:rightN += 1node2 = node2.rightif leftN == rightN:  # 满二叉树result += 2 ** leftN - 1else:  # 非满二叉树result += 1if node.left != None:dq.append(node.left)if node.right != None:dq.append(node.right)return result# 思路2、利用完全二叉树的性质:递归法,重复逻辑:判断每个二叉树是否为满二叉树,满二叉树的节点个数等于2^深度-1,不是满二叉树就按照普通二叉树进行处理def countNodes2(self, root: Optional[TreeNode]) -> int:if root == None:  # 终止条件:简单情况return 0else:  # 重复逻辑leftN, rightN = 0, 0node1, node2 = root, rootwhile node1 != None:leftN += 1node1 = node1.leftwhile node2 != None:rightN += 1node2 = node2.rightif leftN == rightN:  # 满二叉树return 2 ** leftN - 1else:  # 非满二叉树leftN = self.countNodes2(root.left)rightN = self.countNodes2(root.right)return leftN + rightN + 1

110. 平衡二叉树

思路1、一般 层序遍历的基础上,分别获取 每个节点的 两颗子树的高度值,根据平衡二叉树的定义判断——容易写但代码量大
思路2、思路1的递归写法,自顶向底的递归,重复逻辑:重复判断每个二叉树的本身、左右子是否是平衡二叉树,需要把求高度差的函数在每个节点上都运行一遍——复杂度高
思路3、自底向顶的递归,重复逻辑:不断求解左右子树的高度,是平衡二叉树就返回高度,不是平衡二叉树就返回标志-1——这样每个节点的计算高度和判断是否是平衡二叉树都只需要执行一次,算法复杂度为O(N)

class Solution:# 思路1、一般 层序遍历的基础上,分别获取 每个节点的 两颗子树的高度值,根据平衡二叉树的定义判断——容易写但代码量大def isBalanced(self, root: Optional[TreeNode]) -> bool:def getHeight(root: Optional[TreeNode]) -> int:if root == None:return 0dq = deque()dq.append(root)result = 0while len(dq) > 0:size = len(dq)result += 1for _ in range(size):cur = dq.popleft()if cur.left:dq.append(cur.left)if cur.right:dq.append(cur.right)return resultif root == None:return True# 前序遍历rootstack = []stack.append(root)while len(stack) > 0:cur = stack.pop()leftHeight = getHeight(cur.left)rightHeight = getHeight(cur.right)if abs(leftHeight - rightHeight) > 1:return Falseif cur.right:stack.append(cur.right)if cur.left:stack.append(cur.left)return True# 思路2、思路1的递归写法,自顶向底的递归,重复逻辑:重复判断每个二叉树的本身、左右子是否是平衡二叉树# 需要把求高度差的函数在每个节点上都运行一遍——复杂度高def isBalancedRecursive(self, root: Optional[TreeNode]) -> bool:# 求解二叉树的高度# 1、确定函数参数和返回值def getHeight(node: Optional[TreeNode]) -> int:# 2、终止条件if node == None:return 0else:  # 3、单次递归的操作leftN = getHeight(node.left)rightN = getHeight(node.right)return max(leftN, rightN) + 1# 重复逻辑:判断每个二叉树的本身、左右子是否是平衡二叉树# 简单情况if root == None:return Trueelse:  # 重复逻辑leftH = getHeight(root.left)rightH = getHeight(root.right)if abs(leftH - rightH) > 1:return Falseelse:return self.isBalancedRecursive(root.left) and self.isBalancedRecursive(root.right)# 思路3、自底向顶的递归,重复逻辑:不断求解左右子树的高度,是平衡二叉树就返回高度,不是平衡二叉树就返回标志-1——这样每个节点的计算高度和判断是否是平衡二叉树# 都只需要执行一次,算法复杂度为O(N)def isBalancedRecursive2(self, root: Optional[TreeNode]) -> bool:if root == None:return True# 1、函数参数和返回值def getHeight(node: Optional[TreeNode]) -> int:# 2、终止条件if node == None:return 0# 3、确定单次递归的操作leftH = getHeight(node.left)                                # 左if leftH == -1:return -1rightH = getHeight(node.right)                              # 右if rightH == -1:return -1if abs(leftH - rightH) > 1:return -1else:return 1 + max(leftH, rightH)return getHeight(root) != -1

236. 二叉树的最近公共祖先

这道题可能不是很符合本次总结的“与深度相关”,但在实现上是一种“自底向顶的递归”,和“110. 平衡二叉树”的递归也有点类似的,就是很难想到这种解法:特别是简单情况都不太好罗列,需要先把重复逻辑想清楚:不断在二叉树的左右子树中寻找并返回p、q节点

'''
236. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 1:输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1输出:3解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
思路:这道题太难了!自底向顶的递归:重复逻辑:不断在二叉树的左右子树中寻找并返回p、q节点 
'''
class Solution:def lowestCommonAncestor(self, root: Optional[TreeNode], p: Optional[TreeNode], q: Optional[TreeNode]) -> Optional[TreeNode]:# 简单情况if root == None:return Noneelif root == p:  # 因为p、q一定是root中的二叉树节点,因此可以直接进行root与p、q相等的判断,一般是比较它们的节点值return pelif root == q:return qelse:  # 重复逻辑:不断在二叉树的左右子树中寻找并返回p、q节点left = self.lowestCommonAncestor(root.left, p, q)right = self.lowestCommonAncestor(root.right, p, q)if left != None and right != None:  # p、q刚好在以root为根节点的二叉树的左右子树上,此时返回值left、right对应p、qreturn rootelif left != None and right == None:  # p、q刚好在以root为根节点的二叉树的左子树上,此时不为None的返回值left一定是p、q之一return leftelif left == None and right != None:  # p、q刚好在以root为根节点的二叉树的右子树上,此时不为None的返回值right一定是p、q之一return rightelif left == None and right == None:  # 按照题意来说,这种情况是不存在的,可以把这种情况删除掉return None

更多推荐

leetcode分类刷题:二叉树(三、与深度相关的递归)

本文发布于:2023-12-04 04:17:10,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1659729.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:递归   深度   二叉树   leetcode

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!