【算法思想·二叉树】后续篇

本文参考labuladong算法笔记[二叉树心法(后序篇 | labuladong 的算法笔记]

前序位置的代码只能从函数参数中获取父节点传递来的数据,而后序位置的代码不仅可以获取参数数据,还可以获取到子树通过函数返回值传递回来的数据。

那么换句话说,一旦你发现题目和子树有关,那大概率要给函数设置合理的定义和返回值,在后序位置写代码了

652. 寻找重复的子树 | 力扣  | LeetCode  |

给你一棵二叉树的根节点 root ,返回所有 重复的子树 

对于同一类的重复子树,你只需要返回其中任意 一棵 的根结点即可。

如果两棵树具有 相同的结构 和 相同的结点值 ,则认为二者是 重复 的。

示例 1:

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

示例 2:

输入:root = [2,1,1]
输出:[[1]]

示例 3:

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

提示:

  • 树中的结点数在 [1, 5000] 范围内。
  • -200 <= Node.val <= 200
// 函数签名如下
List<TreeNode> findDuplicateSubtrees(TreeNode root);

我来简单解释下题目,输入是一棵二叉树的根节点 root,返回的是一个列表,里面装着若干个二叉树节点,这些节点对应的子树在原二叉树中是存在重复的。

说起来比较绕,举例来说,比如输入如下的二叉树:

首先,节点 4 本身可以作为一棵子树,且二叉树中有多个节点 4:

类似的,还存在两棵以 2 为根的重复子树:

那么,我们返回的 List 中就应该有两个 TreeNode,值分别为 4 和 2(具体是哪个节点都无所谓)。

【思路】

这题咋做呢?还是老套路,先思考,对于某一个节点,它应该做什么

比如说,你站在图中这个节点 2 上:

如果你想知道以自己为根的子树是不是重复的,是否应该被加入结果列表中,你需要知道什么信息?

你需要知道以下两点

1、以我为根的这棵二叉树(子树)长啥样

2、以其他节点为根的子树都长啥样

这就叫知己知彼嘛,我得知道自己长啥样,还得知道别人长啥样,然后才能知道有没有人跟我重复,对不对?好,那我们一个一个来看。

首先来思考,我如何才能知道以自己为根的这棵二叉树长啥样

其实想到这里,就可以判断本题需要在二叉树的后序位置写代码了。

为什么?很简单呀,我要知道以自己为根的子树长啥样,是不是得先知道我的左右子树长啥样,再加上自己,就构成了整棵子树的样子?左右子树的样子,可不就得在后序位置通过递归函数的返回值传递回来吗?

如果你还绕不过来,我再来举个非常简单的例子:计算一棵二叉树有多少个节点。这个代码应该会写吧:

def count(root):if root == None:return 0# 先算出左右子树有多少节点left = count(root.left)right = count(root.right)# 后序位置,子树加上自己,就是整棵二叉树的节点数res = left + right + 1return res

这不就是标准的后序遍历框架嘛,和我们本题在本质上没啥区别对吧。

现在,明确了要用后序遍历,那应该怎么描述一棵二叉树的模样呢?我们后文 序列化和反序列化二叉树 其实写过了,二叉树的前序/中序/后序/层序遍历结果可以描述二叉树的结构。

那么,我就以后序遍历结果作为序列化结果吧,可以通过拼接字符串的方式把二叉树序列化,看下代码:

# 定义:输入以 root 为根的二叉树,返回这棵树的序列化字符串
def serialize(root):# 对于空节点,可以用一个特殊字符表示if root is None:return "#"# 将左右子树序列化成字符串left = serialize(root.left)right = serialize(root.right)# 后序遍历代码位置# 左右子树加上自己,就是以自己为根的二叉树序列化结果myself = f"{left},{right},{root.val}"return myself

我们用非数字的特殊符 # 表示空指针,并且用字符 , 分隔每个二叉树节点值,这属于序列化二叉树的套路了,不多说。

注意我们 myself 是按照左子树、右子树、根节点这样的顺序拼接字符串,也就是后序遍历顺序。因为我们这里的目的是通过序列化唯一描述一棵二叉树的结构,所以你也可以用前序顺序来拼接字符串,但是注意不能用中序顺序,具体原因参见后文 序列化和反序列化二叉树 的总结。

这样,我们第一个问题就解决了,对于每个节点,递归函数中的 myself 变量就可以描述以该节点为根的二叉树。

现在我们解决第二个问题,我知道了自己长啥样,怎么知道别人长啥样?这样我才能知道有没有其他子树跟我重复对吧。

这很简单呀,我们借助一个外部数据结构,让每个节点把自己子树的序列化结果存进去,这样,对于每个节点,不就可以知道有没有其他节点的子树和自己重复了么?

初步思路可以使用 HashSet 记录所有子树的序列化结果,代码如下:

class Solution:# 记录所有子树subTrees = set()# 记录重复的子树根节点res = []def serialize(self, root):if root == None:return "#"# 左右子树的序列化结果left = self.serialize(root.left)right = self.serialize(root.right)# 后序位置,计算以自己为根的二叉树序列化结果myself = left + "," + right + "," + str(root.val)if myself in self.subTrees:# 有人和我重复,把自己加入结果列表self.res.append(root)else:# 暂时没人跟我重复,把自己加入子树集合self.subTrees.add(myself)return myself

但是呢,这有个问题,如果出现多棵重复的子树,结果集 res 中必然出现重复,而题目要求不希望出现重复。

为了解决这个问题,可以把 HashSet 升级成 HashMap,额外记录每棵子树的出现次数:

【python】

class Solution:def __init__(self):# 记录所有子树以及出现的次数self.memo = {}# 记录重复的子树根节点self.res = []# 主函数def findDuplicateSubtrees(self, root: TreeNode) -> List[TreeNode]:self.serialize(root)return self.resdef serialize(self, root):if root is None:return "#"left = self.serialize(root.left)right = self.serialize(root.right)subTree = left + "," + right + "," + str(root.val)freq = self.memo.get(subTree, 0)# 多次重复也只会被加入结果集一次if freq == 1:self.res.append(root)# 给子树对应的出现次数加一self.memo[subTree] = freq + 1return subTree

总结

1、需要比较每一颗子树,要如何遍历?——后序遍历。

2、如何比较每一颗子树的结构和值?——序列化的方式。

3、遇到多个重复子树怎么办?——用字典来存放每个子树出现的次数。

4、重视函数签名——参数是什么?返回值又是什么?

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

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

相关文章

加密货币市场持有与价格波动:CFI调查揭示的趋势与未来展望

自2022年1月以来&#xff0c;消费者金融协会&#xff08;CFI&#xff09;通过六项不同的调查收集了有关加密货币所有权的数据。这些调查旨在了解加密货币的当前持有量和未来购买兴趣&#xff0c;并将这些数据与加密货币市场表现进行对比。结果显示&#xff0c;市场价格与持有量…

【MySQL】MySQL操作介绍

MySQL操作 认识 MySQL什么是 MySQL关系型数据库的组成结构"客户端-服务器"结构 数据库的基本操作创建数据库查看数据库删除数据库使用数据库 数据类型整型浮点类型字符串类型日期类型总结 表的操作创建表查看表查看表的信息删除表 数据的基础操作插入数据指定列插入全…

计算机网络:http协议

计算机网络&#xff1a;http协议 一、本文内容与前置知识点1. 本文内容2. 前置知识点 二、HTTP协议工作简介1. 特点2. 传输时间分析3. http报文结构 三、HTTP版本迭代1. HTTP1.0和HTTP1.1主要区别2. HTTP1.1和HTTP2主要区别3. HTTPS与HTTP的主要区别 四、参考文献 一、本文内容…

设计模式-行为型模式-迭代器模式

1.迭代器模式的定义 迭代器模式提供一种对容器对象中的各个元素进行访问的方法&#xff0c;而不需要暴露该对象的内部细节&#xff1b; 在软件系统中&#xff0c;容器对象有两个职责&#xff1a;一是存储数据&#xff0c;二是遍历数据&#xff1b;从依赖性上看&#xff0c;前者…

高效诊断Linux性能问题

从uptime命令开始&#xff1b;这里的关键指标是平均负载&#xff0c;它显示了过去 1分钟&#xff0c;5分钟和15分钟内正在运行或等待资源的进程平均数量&#xff1b;如果这些数字持续高于CPU内核数&#xff0c;则可能表明进程正在争夺资源&#xff0c;提示我们使用其他工具深入…

多语言ASO – 本地化的10个技巧

ASO优化是一个复杂的领域&#xff0c;即使你只关注讲英语的用户。如果您想面向国际受众并在全球范围内发展您的应用程序业务&#xff0c;您必须在App Store和Google Play Store上本地化应用程序的产品页面。不过&#xff0c;应用程序商店本地化的过程也有很多陷阱。 应用商店本…

100个视频如何转换成1个二维码

使用场景描述&#xff1a;有50-100个视频&#xff0c;要实现扫一个二维码&#xff0c;就可以完整观看这50-100个视频的内容&#xff0c;这种情况下&#xff0c;可以使用列表专辑二维码功能来轻松实现。 使用步骤 STEP1 注册帐号 使用视频专辑列表二维码&#xff0c;您需要注册…

TPM管理培训为何难以落地?原因解析与解决之道

近年来&#xff0c;TPM管理被视为提升设备效率、减少故障率、降低生产成本的关键。然而&#xff0c;尽管TPM的理念被广泛接受&#xff0c;其在实践中的落地却常常面临各种挑战。本文&#xff0c;深圳天行健企业管理咨询公司将深入解析TPM管理培训难以落地的根本原因&#xff0c…

Linux驱动(五):Linux2.6驱动编写之设备树

目录 前言一、设备树是个啥&#xff1f;二、设备树编写语法规则1.文件类型2.设备树源文件&#xff08;DTS&#xff09;结构3.设备树源文件&#xff08;DTS&#xff09;解析 三、设备树API函数1.在内核中获取设备树节点&#xff08;三种&#xff09;2.获取设备树节点的属性 四、…

2000-2022年中国河流水系变化矢量数据集(矢量/30m)含:河道水面宽度、水面中心线、水系网络节点

2000-2022年中国河流水系变化矢量数据集 数据介绍 河网水系是是受气候变化和人类活动影响最显著的水生态环境之一。联合国可持续发展目标6&#xff08;SDG 6&#xff09;亚目标SDG 6.6.1是指与水有关的生态系统范围随时间的变化。本数据产品面向SDG 6.6.1&#xff0c;利用卫星…

盘点:当养生茶遇上互联网,都有哪些打法?

健康行业电商大战早已拉开序幕&#xff0c;作为健康行业的一个大类——养生茶还能缺席么&#xff1f;三好夫人、同仁堂、东韵、九芝堂、余庆堂等等各路豪杰齐聚养生茶电商&#xff0c;看他们如何各显神通吧&#xff01; 三好夫人——一生只送一人 三好夫人以爱之名创立&#x…

网络编程入门-实现服务器与客户端通讯

概念学习 TCP概念&#xff1a; TCP&#xff08;Transmission Control Protocol&#xff09;协议指的是传输控制协议&#xff0c;是一个面向连接的传输协议&#xff0c;他是一个能提供高可靠性的通信协议&#xff0c;所谓高可靠性指的是数据无丢失、数据无误、数据无失序、数据…

Keil导入包出错

1.菜单栏找不到GD系列&#xff1f; 随便新建一个工程&#xff0c;将project用记事本打开后如图2所示。再将别人给的代码工程用记事本打开&#xff0c;发现别人给的工程少了这两行&#xff0c;所以复制粘贴到别人给的工程记事本中&#xff0c;保存刷新后重新打开&#xff0c;就…

剑指offer JZ23 链表中环的入口结点

问题描述&#xff1a; 给定一个长度为n的链表&#xff0c;首先判断其是否有环&#xff0c;然后找到环的入口。 要求&#xff1a;空间复杂度 O(1)&#xff0c;时间复杂度 O(n)。 思路&#xff1a; 1. 投机一点的做法 从头遍历链表&#xff0c;如果有环&#xff0c;那么有些节…

解读:以RTC为基,AI为脑的“超拟人”AI实时互动解决方案

我们打造了一款满足想象与应用的智能体——AI实时互动。 谈谈AI智能体 当AI变得足够聪明时&#xff0c;用户与AI的交互将变得真实自然。于是&#xff0c;构建高拟真AI与用户的实时交互&#xff0c;已经成为企业提升数智化生产力的新思路。 在这个交互过程中&#xff0c;存在一…

@开发者极客们,网易2024低代码大赛来啦

极客们&#xff0c;网易云信拍了拍你 9月6日起&#xff0c;2024网易低代码大赛正式开启啦&#xff01; 低代码大赛是由网易主办的权威赛事&#xff0c;鼓励开发者们用低代码开发的方式快速搭建应用&#xff0c;并最终以作品决出优胜。 从2022年11月起&#xff0c;网易低代码大赛…

python_openCV_计算图片中的区域的黑色比例

希望对原始图片进行处理&#xff0c;然后计算图片上的黑色和白色的占比 上图&#xff0c; 原始图片 import numpy as np import cv2 import matplotlib.pyplot as pltdef cal_black(img_file):#功能&#xff1a; 计算图片中的区域的黑色比例#取图片中不同的位置进行计算&…

Qt:关于使用player->state()导致的程序崩溃

前言 最近想做一个白噪声播放器&#xff0c;中间就用到了QMediaplayer这个类&#xff0c;其中遇到两个问题&#xff0c;一个是调用player->duration()第一次获取媒体时长为0的问题(这个问题留到下一个文章去说)&#xff1b;还有一个就是未初始化好就调用player->state()…

解决Win10版Township进度保存问题

解决Win10版Township进度保存问题 问题描述问题分析解决步骤1.WinR打开运行&#xff0c;输入regedit点击确定打开注册表2.进入注册表“计算机\HKEY_CURRENT_USER\Software\Classes\LocalSettings\Software\Microsoft\Windows\CurrentVersion\AppContainer\Mappings”目录3.在这…

逆向学习系列(一)安装模拟器

从今天开始&#xff0c;学习逆向APP的知识并记录。 首先&#xff0c;从最简单的环境搭建开始&#xff0c;我的环境&#xff08;LENOVO&#xff09;&#xff0c;win7&#xff0c;64位。 &#xff08;一&#xff09;安装mumu模拟器。 官网地址&#xff1a;MuMu模拟器官网_安卓…