【代码随想录day15】110.平衡二叉树 ● 257. 二叉树的所有路径 ● 404.左叶子之和

110.平衡二叉树

问题

题目链接:https://leetcode.cn/problems/balanced-binary-tree/
给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
示例:
在这里插入图片描述

输入:root = [1,2,2,3,3,null,null,4,4]
输出:false

解答

这里强调一波概念:

二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。
二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。

先用递归写一个返回最大深度的函数,然后再用递归判断根节点的左右子树是否相差大于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 maxnode(self, root):if not root: return 0return 1+max(self.maxnode(root.left), self.maxnode(root.right))def isBalanced(self, root: Optional[TreeNode]) -> bool:if not root:return Truea = abs(self.maxnode(root.left) - self.maxnode(root.right))if a > 1:return Falsereturn self.isBalanced(root.left) and self.isBalanced(root.right)

随想录上的解答:

class Solution:def isBalanced(self, root: TreeNode) -> bool:if self.get_height(root) != -1:return Trueelse:return Falsedef get_height(self, root: TreeNode) -> int:# Base Caseif not root:return 0# 左if (left_height := self.get_height(root.left)) == -1:return -1# 右if (right_height := self.get_height(root.right)) == -1:return -1# 中if abs(left_height - right_height) > 1:return -1else:return 1 + max(left_height, right_height)
class Solution:def isBalanced(self, root: TreeNode) -> bool:return self.height(root) != -1def height(self, node: TreeNode) -> int:if not node:return 0left = self.height(node.left)if left == -1:return -1right = self.height(node.right)if right == -1 or abs(left - right) > 1:return -1return max(left, right) + 1

此处用了python3.8的海象表达式(walrus operator): if (left_height := self.get_height(root.left)) == -1:
海象表达式(Walrus operator)是Python 3.8版本引入的一种新的语法特性。它使用了:=这个符号,允许在表达式中进行变量的赋值操作。

通常情况下,Python中的变量赋值是在等号的左侧进行的,例如x = 10。而海象表达式允许我们在表达式的右侧进行变量赋值,将结果赋给一个新的变量。这对于简化代码和增加可读性非常有用。
一个常见的用法是在条件语句中,例如:

if (n := len(my_list)) > 10:print(f"List length is {n} which is greater than 10.")

在上面的代码中,海象表达式(n := len(my_list))用于同时计算列表的长度并将结果赋值给变量n。然后,我们可以直接在条件语句中使用变量n进行比较操作。
海象表达式还可以在其他情况下使用,例如循环语句和列表推导式中,以简化代码和提高可读性。
需要注意的是,海象表达式是在Python 3.8版本中引入的新特性,因此在较旧的Python版本中不可用。在使用海象表达式时,需要确保代码运行在支持该特性的Python版本上。

迭代思路也类似,先用一个函数求最大高度,然后采用栈模拟后续遍历判断每个节点的子树是否平衡。
当然此题用迭代法,其实效率很低,因为没有很好的模拟回溯的过程,所以迭代法有很多重复的计算。

class Solution:def isBalanced(self, root: Optional[TreeNode]) -> bool:if not root:return Trueheight_map = {}stack = [root]while stack:node = stack.pop()if node:stack.append(node)stack.append(None)if node.left: stack.append(node.left)if node.right: stack.append(node.right)else:real_node = stack.pop()left, right = height_map.get(real_node.left, 0), height_map.get(real_node.right, 0)if abs(left - right) > 1:return Falseheight_map[real_node] = 1 + max(left, right)return True

257. 二叉树的所有路径

问题

题目链接:https://leetcode.cn/problems/binary-tree-paths/
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。

叶子节点 是指没有子节点的节点。
示例:
在这里插入图片描述

输入:root = [1,2,3,null,5]
输出:[“1->2->5”,“1->3”]

解答

自己想这题的时候思路很简单,递归只需要判断是否为叶子节点就行,不是叶子节点则进入下一层,即该节点的子节点。但会遇到一个问题,那就是如何复制已存在的路径,看了解答才知道可以用copy package,那么题目就很简单了。

# 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
# copy可以用来复制路径,避免对上一级的path产生影响
import copyclass Solution:def markpath(self, root, path, result):if not root: return Nonepath.append(root.val)if not root.left and not root.right:result.append('->'.join(map(str, path))) #map(fucntion, iteratable)if root.left:self.markpath(root.left, copy.copy(path), result)if root.right:self.markpath(root.right, copy.copy(path), result)# copy函数是复制了一个path,而不是在原path上修改,因此回不回溯都不影响#path.pop()def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:if not root: return []result = []self.markpath(root, [], result)return result

但是由于python里有copy库,因此是否回溯就不重要了,因为永远不会对上一级的path产生影响,所以以下补上C++里的方法,帮助更好理解回溯

这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。

在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。

前序遍历以及回溯的过程如图:
在这里插入图片描述

在这里插入图片描述
与python里不同,c++没有copy函数复制path,因此需要采用一个vector来记录path,所以要把vector 结构的path转为string格式,再把这个string 放进 result里。同时当遇到叶子节点的时候,即递归结束,要开始回溯了,方法就是删除path最后一个元素。同时要注意,回溯和递归是一一对应的,有一个递归,就要有一个回溯,即递归要和回溯在同一个括号内。

class Solution {
private:void traversal(TreeNode* cur, vector<int>& path, vector<string>& result) {path.push_back(cur->val); // 中,中为什么写在这里,因为最后一个节点也要加入到path中 // 这才到了叶子节点if (cur->left == NULL && cur->right == NULL) {string sPath;for (int i = 0; i < path.size() - 1; i++) {sPath += to_string(path[i]);sPath += "->";}sPath += to_string(path[path.size() - 1]);result.push_back(sPath);return;}if (cur->left) { // 左 traversal(cur->left, path, result);path.pop_back(); // 回溯}if (cur->right) { // 右traversal(cur->right, path, result);path.pop_back(); // 回溯}}public:vector<string> binaryTreePaths(TreeNode* root) {vector<string> result;vector<int> path;if (root == NULL) return result;traversal(root, path, result);return result;}
};

补充:
python其实也可以不用copy函数,同样可以与c++对应,修改如下:

from typing import List, Optionalclass Solution:def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:if not root:return []result = []self.generate_paths(root, [], result)return resultdef generate_paths(self, node: TreeNode, path: List[int], result: List[str]) -> None:path.append(node.val)if not node.left and not node.right:result.append('->'.join(map(str, path)))if node.left:self.generate_paths(node.left, path, result)path.pop()if node.right:self.generate_paths(node.right, path, result)path.pop()

再修改一下:
from typing import List, Optional

class Solution:def binaryTreePaths(self, root: Optional[TreeNode]) -> List[str]:if not root:return []result = []self.generate_paths(root, [], result)return resultdef generate_paths(self, node: TreeNode, path: List[int], result: List[str]) -> None:path.append(node.val)if not node.left and not node.right:result.append('->'.join(map(str, path)))if node.left:self.generate_paths(node.left, path, result)#path.pop()if node.right:self.generate_paths(node.right, path, result)#path.pop()path.pop()

模拟一下下图的过程吧:
在这里插入图片描述

首先定义一下:if1,if2,if3分别表示generate_paths里的三个判断if,以及下文的第几个递归表示序号行执行的递归,不表示实际递归层数。

  1. cur=1,path=[1],满足if2,进入if2的下一层递归2
  2. cur=2,path=[1,2],满足if2,进入if2的下一层递归3
  3. cur=3,path=[1,2,3],满足if1,输出路径,且不满足if2,if3,因此第3个递归执行结束
  4. 第3个递归执行完毕,回到第2个递归,执行剩下的path.pop(),path=[1,2]
  5. 但此时第2个递归还没结束,因为也满足if3
  6. cur=2,path=[1,2],满足if3,进入if3的下一层递归
  7. cur=4,path=[1,2,4],满足if2,进入if2的下一层递归
  8. cur=5,path=[1,2,4,5],满足if1,输出路径
  9. 第8个递归执行完毕,回到上一层递归,即第7个递归,执行剩下的pop,path=[1,2,4]
  10. cur=4,path=[1,2,4],满足if3,进入if3的下一层递归
  11. cur=6,path=[1,2,4,6],满足if1,输出路径
  12. 第11个递归执行结束,退回到第10个,执行剩下的pop,path=[1,2,4]
  13. 由于第10个递归if3也判断完毕,因此第10个递归执行结束,退回上一层即第6,执行pop,path=[1,2]
  14. 同理第6也执行完毕,退回到第1个,执行pop,path=[1]
  15. cur=1,path=[1],满足if3,进入if3的下一层递归,之后的右子树同理,输出path=[1,7,8]后不断回溯到第1个迭代,由于此时cur=root=1的左右节点都处理完了,因此整个递归过程结束。

顺便用chatGPT解释了一下为啥要path.pop():
在给左子树递归调用 generate_paths 之后,需要回溯到上一层继续遍历其他分支。由于 path 列表是作为参数传递的,如果不进行回溯操作,那么左子树的遍历过程中添加的节点值将会一直保留在 path 列表中,影响到右子树的遍历。

通过调用 path.pop(),可以在递归回溯到上一层时,将 path 列表中的当前节点值移除,恢复到上一层的状态。这样,在处理右子树时,就能够从一个空的 path 列表开始,不受之前遍历过的节点的影响。

简而言之,path.pop() 的作用是确保在回溯到上一层时,将当前节点从路径列表中移除,以便在处理其他分支时得到正确的路径。

404.左叶子之和

问题

题目链接:https://leetcode.cn/problems/sum-of-left-leaves/
计算给定二叉树的所有左叶子之和。
示例:
在这里插入图片描述

输入: root = [3,9,20,null,null,15,7]
输出: 24
解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24

解答

这道题目要求左叶子之和,其实是比较绕的,因为不能判断本节点是不是左叶子节点。此时就要通过节点的父节点来判断其左孩子是不是左叶子了。
此时有两个判断条件,首先该节点没有子节点,然后该节点是上一个节点的左节点。因此判断停止条件是not root.left.left and not root.left.right。但是停止之后返回什么呢,肯定不能只返回root.left.val,因为如果该root存在右节点,我们并没有对右节点的进行判断,因此我们将root.right作为下一层的root继续输入,因此我们要返回root.left.val+function(root.right)

# 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 sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:if not root: return 0# 排除没有左节点的情况,但是右节点没判断# 因此我们返回右子树的所有左叶子的和if not root.left: return self.sumOfLeftLeaves(root.right)# 同理,右节点没判断,因此加上右子树的所有左叶子和if (not root.left.left) and (not root.left.right):return root.left.val + self.sumOfLeftLeaves(root.right)return self.sumOfLeftLeaves(root.left)+self.sumOfLeftLeaves(root.right)

用chatGPT解释了一下上述代码:
这段代码实现了计算二叉树中所有左叶子节点值的和。下面是代码的解释:

1.首先,检查根节点是否为空,如果为空,表示当前子树为空树,直接返回0作为和的初始值。

2.然后,检查根节点的左子节点是否为空,如果为空,表示当前子树没有左子树,那么就递归地计算右子树中的左叶子节点值的和(通过调用 self.sumOfLeftLeaves(root.right))。

3.如果根节点的左子节点存在,再进一步检查左子节点是否为叶子节点(即没有左子节点和右子节点)。如果是叶子节点,那么将左子节点的值加上右子树中左叶子节点值的和,作为最终的和。

4.如果根节点的左子节点不是叶子节点,那么就递归地计算左子树和右子树中左叶子节点值的和,并将两者的和返回。

这段代码利用了递归的思想,通过不断地将问题拆分成更小的子问题来求解。它按照以下步骤计算左叶子节点值的和:

如果当前子树为空树,则和为0。
如果当前子树没有左子树,则计算右子树中的左叶子节点值的和。
如果当前子树有左子树,并且左子节点为叶子节点,则将左子节点的值加上右子树中左叶子节点值的和,作为最终的和。
如果当前子树有左子树,并且左子节点不是叶子节点,则分别计算左子树和右子树中左叶子节点值的和,并将两者的和返回。
通过递归地处理每个节点,直到遍历完整棵二叉树,最终得到所有左叶子节点值的和作为结果返回。

随想录上的结构更清晰一些:

class Solution:def sumOfLeftLeaves(self, root: Optional[TreeNode]) -> int:if not root:return 0# 检查根节点的左子节点是否为叶节点if root.left and not root.left.left and not root.left.right:left_val = root.left.valelse:left_val = self.sumOfLeftLeaves(root.left)# 递归地计算右子树左叶节点的和right_val = self.sumOfLeftLeaves(root.right)return left_val + right_val

迭代法思路一样,只不过用循环和栈实现:

class Solution:def sumOfLeftLeaves(self, root: TreeNode) -> int:"""Idea: Each time check current node's left node. If current node don't have one, skip it. """stack = []if root: stack.append(root)res = 0while stack: # 每次都把当前节点的左节点加进去. cur_node = stack.pop()if cur_node.left and not cur_node.left.left and not cur_node.left.right: res += cur_node.left.valif cur_node.left: stack.append(cur_node.left)if cur_node.right: stack.append(cur_node.right)return res

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/38819.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot自动装配串讲

目录 前言一. 基础概念1-1. Spring1-2. SpringBoot 二. 自动装配概览2-1. 效果&#xff08;目的&#xff09;2-2. 猜想2-3. SpringBoot的实现方案2-4. 对比及分析2-4-1. starter里为什么没有pom文件2-4-2. 配置类为什么没写在starter里 三. 自动装配细节3-1. 流程图3-2. 各部分…

告别抠图!海量免抠PNG,任你选

无论处理图片还是做PPT&#xff0c;经常会用到透明背景的图片素材&#xff0c;往往这种时候就需要进行抠图加 工。对PS图技术不佳的小伙伴而言&#xff0c;要抠出一张完美的图片并非易事。但也不是难事&#xff0c;只要你 有免抠PNG素材网&#xff08;搜图114 www.sotu114.co…

自己用ps抠图标

大家进入项目阶段已经有差不多一个月了&#xff1b;从简单数据库分析到慢慢的调试页面&#xff1b;再到 灵活的进行增删查改&#xff1b;相信在这一个月里大家的代码技术都有了一定的提高&#xff1b;相信对于以前学的现在是更加熟练了。对于不会的&#xff0c;可以通过网上查找…

不会PS怎么抠图换背景?赶紧收藏这3个好用的一键抠图神器

在现代社交媒体的世界里&#xff0c;给一张图片抠图换背景已经成为了互联网上很普遍的需求&#xff0c;比如有些朋友可能需要在社交媒体上分享自己的照片或制作一些创意性的设计作品&#xff0c;抠图换背景就可以帮助把图片创造出更好的视觉效果。一提到抠图去除背景&#xff0…

抠图软件哪个好用?这些软件你了解吗?

我们在抠取图片中的元素时&#xff0c;偶尔需要将图片中的人物抠出来。比如通过抠人像的方式&#xff0c;给证件照更换背景&#xff1b;或者制作搞怪照片&#xff0c;玩法多样。不过我们需要选择一款适合自己的人像抠图软件&#xff0c;所以人像抠图软件哪个好&#xff1f;快往…

GIMP:利用蒙板工具实现人像抠图

GIMP&#xff1a;利用蒙板工具实现人像抠图 利用蒙板工具进行抠图简单介绍方法步骤1.打开图像2.复制图层3.选中图层4.将图层改为单色5.人像与背景分离6.反相显示7.人像部分描白8.添加图层蒙板9.粘贴白色人像轮廓10.图层不可视11.解决人像范围不正确12.随意更换图像背景 利用蒙板…

抠图软件哪个好用又免费?快来看看这几款软件

相信大家都使用过社交软件吧&#xff0c;有时候我们在里面看到的那些精美的图片、有趣的视频&#xff0c;大部分都是经过软件处理出来的。而当我们要在社交软件上分享自己的日常时&#xff0c;也会自带修图、滤镜、抠图等功能&#xff0c;但是用起来有时会难以达到自己想要的那…

PHP Imagick 去背景 (抠图专用)

最近接到一个项目需要用到电子签章。 需求&#xff1a;章、人员签名&#xff0c;盖在白纸或手写在白纸上通过拍照的方式上传到系统。 前期是公司小妹通过PS把图片扣出来&#xff0c;弄成透明的背景然后上传。 这样每次有新增都需要人工处理&#xff0c;不方便和智能&#xff0c…

开发了一个抠图/去背景应用

jr们早上好 iPhone 的 iOS 16有个很酷的功能&#xff0c;长按照片就能把其中的拍摄主体提取出来&#xff0c;抠图过程比一般的抠图App方便&#xff0c;精细度也更高。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KIlpLyow-1680141413142)(https…

抠图应用程序设计(三)——QT用户界面的实现

简介 ​ 本设计的GUI其实是由一个用户界面demo开发而成&#xff0c;主要由弹窗以及主界面组成。弹窗主要用于提示用户操作&#xff0c;为用户提供选择&#xff1b;主界面用于交互功能的实现。 外观设计 ​ 外观设计部分大部分在Qt Designer上完成。将所需控件按照方案论证中…

【虚幻引擎UE】UE5 AR初体验之静态动态模型加载

UE5的AR初体验之静态动态两种模型加载 基于配置好AR环境&#xff08;参考另一篇文章&#xff09; 先## 标题简单了解一下它的项目结构 这里的brush就是我们的操作空间范围 官方模板可以实现平面识别&#xff0c;控制对象的旋转和缩放。 本文主要说明在AR项目中实现模型加载…

在虚幻引擎5中构建你的首款游戏 - 04 - 地形和草地

在虚幻引擎5中构建你的首款游戏 - 04 - 地形和草地 前言介绍: 原版地址: << [功能亮点]在虚幻引擎5中构建你的首款游戏(官方字幕)_哔哩哔哩_bilibili >> << https://www.bilibili.com/video/BV1M34y1x7tc >> 官网地址: << Your First Game In Un…

虚幻引擎(UE5)-大世界分区WorldPartition教程(三)

文章目录 前言LevelInstance的使用1.ALevelInstance2.选择Actor创建关卡3.运行时加载LevelInstance 总结 上一篇&#xff1a;虚幻引擎(UE5)-大世界分区WorldPartition教程(二) 前言 在制作大关卡时&#xff0c;可能会遇到这样一种情况&#xff0c;就是关卡中的某些Actor会重复…

虚幻引擎5 学习笔记2

一、visual studio 2019添加工作负荷 选中工具-获取工具和功能 点击工作负载-勾选要添加的负载——“使用c的桌面开发”和“使用c的游戏开发” “” 二、创建项目 创建一个c的项目&#xff0c;初学者包也勾上。名字不要有中文。 报错&#xff1a; 提示缺少了net程序&…

在虚幻引擎5中构建你的首款游戏 - 05 - 岩石和植物

在虚幻引擎5中构建你的首款游戏 - 05 - 岩石和植物 前言介绍: 原版地址: << [功能亮点]在虚幻引擎5中构建你的首款游戏(官方字幕)_哔哩哔哩_bilibili >> << https://www.bilibili.com/video/BV1M34y1x7tc >> 官网地址: << Your First Game In Un…

虚幻引擎(UE5)-大世界分区WorldPartition教程(四)

文章目录 前言一、Data Layers的使用1.添加Actor到Data Layers2.运行时处理 总结 上一篇&#xff1a;虚幻引擎(UE5)-大世界分区WorldPartition教程(三) 前言 Data Layers&#xff08;UE4中叫Layers&#xff09;用于将Actor划分到不同的Layer中&#xff0c;通过在编辑器和运行…

UE5使用MetaHuman构建超现实的角色

使用免费的MetaHuman创造者应用程序为虚幻构建超现实的角色。 流派:电子学习| MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;48.0 KHz 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09;|大小解压后:1.66 GB 含课程文件|时长:1h 49…

虚幻引擎5改变了游戏,并与Perforce原生集成

游戏发展|版本控制 作者&#xff1a;Ryan L Italien 虚幻引擎是当下流行的游戏引擎之一。虽然很多团队喜欢UE4&#xff0c;但虚幻引擎5 (UE5)的抢先版本包含了一些期待已久的改进(加上一些令人惊叹的新功能)。 这篇文章将分析我们为什么对虚幻引擎 5 感到如此兴奋。此外&…

UE5 最新动态虚幻引擎全新版本引爆互联网

自 1998 年上市以来&#xff0c;虚幻引擎一直是顶级游戏开发工具之一。一些史上最大型游戏 —《杀出重围》和《生化奇兵》系列、《火箭联盟》、《堡垒之夜》等等 — 均使用该引擎的不同迭代版本进行构建。 随着电影和电视行业日益认识到虚拟引擎的作用&#xff0c;甚至在游戏业…

UE4 回合游戏项目 22- 控制新角色

在上一节&#xff08;UE4 回合游戏项目 21- 添加多种类型的敌人&#xff09;基础上新添加一个玩家角色 效果&#xff1a; 步骤&#xff1a; 1.打开进阶游戏资源&#xff0c;解压“回合迁移_第七节&#xff08;只是新人物包&#xff09;” 2.解压后双击打开工程 3.选中“ziyuan…