算法通过村第八关-树(深度优先)青铜笔记|经典算法题目

文章目录

  • 前言
  • 1. 二叉树里面的双指针
    • 1.1 判断两棵树是否相同
    • 1.2 对称二叉树
    • 1.3 合并二叉树
  • 2. 路径专题
    • 2.1 二叉树的所有路径
    • 2.2 路径总和
  • 3. 翻转的妙用
  • 总结


前言


提示:人类的底里是悲伤,我们都在用厚重的颜料,覆盖那些粗糙的线稿。--张皓宸《抬头看二十九次月亮》

前面的练习才是开始,这理才是真正的进入算法的门槛,来迎接下一波挑战吧。

1. 二叉树里面的双指针

所谓的双指针就是定义了两个变量,在二叉树中有时候也需要至少定义两个变量才能解决问题,这两个指针可能针对一颗树,也可能针对两棵树,我们这里就称他为”双指针“吧。这些问题一般是关于对称、翻转、合并等类型相关,我们接下来就看一些高频出现的问题吧。

1.1 判断两棵树是否相同

参考题目介绍:100. 相同的树 - 力扣(LeetCode)
在这里插入图片描述
在这里插入图片描述
这个貌似很好就容易实现了,两个二叉树同时进行前序遍历,先判断根节点是否相同,如果相同再分别判断左右子节点是否相同,判断的过程中只要存在一个不相同的就返回false,如果全部相同就返回true。其实也就是这样的。

	/*** 判断两个二叉树是否相同* @param p* @param q* @return*/public static boolean isSameTree(TreeNode p, TreeNode q) {// 校验参数// 如果两个节点为空 肯定相同if (p == null && q == null) {return true;}// 如果两个中有一个不为空 一定不相同if (p == null || q == null) {return false;}// 当然如果 数据不同  一定不相同if(p.val != q.val){return false;}// 走到这一步 说明p和q的节点是完全相同的,然后接着遍历return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);}

这里你也可以试一试广度优先,这样写会怎么样,感兴趣可以试试。

1.2 对称二叉树

参考题目介绍:101. 对称二叉树 - 力扣(LeetCode)
在这里插入图片描述
在这里插入图片描述
如果树是镜像的,看看这个图,更加清晰:

在这里插入图片描述
因为我们要通过递归函数的返回值来判断这两个子树内测节点是否相同,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。这里的关键还是比较和如何处理结束的条件。单层递归的逻辑就是处理左右节点不为空且数值相同的情况。

  1. 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子
  2. 比较二叉树内侧是否对称:传入左节点的右孩子,右节点的左孩子
  3. 如果左右都对称就返回true,有一侧不对称就返回false。

接下来就是合并和简化代码的过程了:

	/*** 判断是否是一颗对称的二叉树* @param root* @return*/public static boolean isSymmetric(TreeNode root) {// 校验参数if (root == null){return  true;}return check(root.left,root.right);}private static boolean check(TreeNode p, TreeNode q) {if (p == null && q == null){return true;}if (p == null || q == null){return false;}if (p.val != q.val){return false;}return check(p.left, q.right) && check(p.right, q.left);}

1.3 合并二叉树

参考题目介绍:617. 合并二叉树 - 力扣(LeetCode)

在这里插入图片描述

在这里插入图片描述
两个二叉树的对应节点可能存在一下三种情况,对于每种情况的使用不同的合并方式。

  1. 如果两个二叉树的对应节点都为空,则合并的二叉树的对应节点也为空;
  2. 如果两个二叉树的对应节点只有一个为空空,则合并后的二叉树的对应节点为其中的非空节点;
  3. 如果两个二叉树的对应节点都不为空,则合并后的二叉树的对应节点的值为两个二叉树的对应值之和,此时需要显性合并两个节点。

对一个节点进行合并之后,还要对该节点的左右子树分别进行合并;

代码如下:

	/*** 合并两个二叉树** @param t1* @param t2* @return*/public static TreeNode mergeTrees(TreeNode t1, TreeNode t2) {// 校验参数if (t1 == null){return t2;}if (t2 == null){return t1;}// 建立一个新的树rootTreeNode  merged = new TreeNode(t1.val + t2.val);merged.left = mergeTrees(t1.left,t2.left);merged.right = mergeTrees(t1.right,t2.right);return merged;}

如果这到题目没有想明白的话,就带入例子看看验证一下。

看了这么多,我们是不是也可以造一个提来看看:前面我们研究了两颗树相等和一棵树对称的情况,我们这里可以造一个判断题,怎么证明两棵树是否对称。想一下这个要怎么写💡
在这里插入图片描述

2. 路径专题

关于二叉树有些题目与路径有关,我们这里好好看下。回溯这个看起来头疼的问题,我们先放在后面,看看这个路径专题哈哈🥰

2.1 二叉树的所有路径

参考题目介绍:257. 二叉树的所有路径 - 力扣(LeetCode)
在这里插入图片描述
在这里插入图片描述

我们可以注意有几个叶子节点,有几个就说明有几条路径,这么问题就转换成了怎么找叶子节点了。我们知道深度优先搜索就是从根节点出发一直寻到叶子节点,这里我们可以先判断当前节点是不是叶子节点,再决定是不是向下走,如果是叶子节点,我们就增加一条路径,就像下图一样。
在这里插入图片描述
这里还有个问题,当得到一个叶子节点容易,这时候怎么知道它所在的完整路径是什么呢?例如上图中的D之后,怎么知道前面还有A和C呢?这里简单,我们增加一个String类型的变量,访问每个节点的时候先将他存入String中,到叶子节点的时候再添加到集合中:

具体代码如下:

	/*** 二叉树的所有路径* @param root* @return*/public static List<String> binaryTreePaths(TreeNode root) {// 创建空间List<String> res = new ArrayList<String>();// 广度优先搜索dfs(root, "", res);return res;}private static void dfs(TreeNode root, String path, List<String> res) {// 终止条件if (root == null) {return;}// 到达叶子节点if (root.left == null && root.right == null) {res.add(path + root.val);return;}// 分别遍历左右子树dfs(root.left, path + root.val + "->", res);dfs(root.right, path + root.val + "->", res);}

这个题目是回溯的基础入门问题,我们后面再讲,感兴趣的同学可以带入例子看看。

2.2 路径总和

参考题目介绍:112. 路径总和 - 力扣(LeetCode)

在这里插入图片描述

在这里插入图片描述
本题目询问是否存在从当前节点root到叶子节点的路径,满足其路径和为sun,假定从根节点到当前节点的值之和为val,我们可以将这个大问题转换成为一个小问题:是否存在从当前节点的子节点到叶子的路径,其满足路径和为sum - val。

不难看出这个问题满足递归的性质,若当前节点就是叶子节点,那么我们直接判断sum是否等于val即可(因为路径和已经确定,就是当前节点的值,我们只要判断该路径和是否满足条件)。若当前节点不是叶子节点,需要继续递归询问它的子节点是否满足条件。

代码就好写多了😎:

	/***  路径总和** @param root* @param sum* @return*/public static boolean hasPathSum(TreeNode root, int sum) {// 终止条件if(root == null){return false;}if (root.left == null && root.right == null){return sum == root.val;}// 接着遍历左右子树boolean left = hasPathSum(root.left,sum - root.val);boolean right = hasPathSum(root.right,sum - root.val);return left || right;}

学了这道题,感性取的话可以挑战一下路径总和II。这里必考虑回溯问题

推荐题目⭐⭐⭐⭐

113. 路径总和 II - 力扣(LeetCode)

3. 翻转的妙用

参考题目介绍:226. 翻转二叉树 - 力扣(LeetCode)

在这里插入图片描述
这个题目同类型题:剑指 Offer 27. 二叉树的镜像 - 力扣(LeetCode)简直是一摸一样。当然了,根据上图,我们可以发现想要翻转树,就是把每个节点的左右孩子交换一下。关键在于遍历顺序,前中后你觉的那个更适合呢?遍历的过程中取翻转每一个节点的左右孩子就可以达到整体翻转的效果。注意只要把每一个节点的左右孩子翻转一下就可以达到整体翻转的效果。

这是一道很经典的二叉树问题。显然,我们从根节点开始,递归对树进行遍历,并从叶子节点先开始翻转。如果当前遍历到的节点root的左右两颗子树都已经翻转过,那么我们只需要交换两个子树的位置,就可以完成以root为根节点的整棵树的翻转。

话不多说先看看前序交换怎么实现的,代码如下:

	/*** 前序遍历的二叉树翻转* @param root* @return*/public TreeNode invertTree(TreeNode root) {// 终止条件if (root == null){return  null;}// 先处理根节点TreeNode temp = root.left;root.left = root.right;root.right = temp;// 然后左右子树invertTree(root.left);invertTree(root.right);return root;}

那么后序遍历呢?

	/*** 后序遍历的二叉树翻转* @param root* @return*/public TreeNode invertTree(TreeNode root) {// 终止条件if (root == null) {return null;}// 先左右TreeNode left = invertTree(root.left);TreeNode right = invertTree(root.right);root.left = right;root.right = left;return root;}

这道题目使用前序和后序遍历都可以,你猜对了吗?💕主要区别就是,前序是先处理子节点,属于自顶向下,后序是先处理叶子节点最后处理自己,属于自下而上的。我们来看这个图:
在这里插入图片描述
当然了,本题还可以使用层序遍历实现,核心思想就是元素出队时,先将其左右孩子不直接入队,而是反转后再放进去,顺便也看下代码吧:

	/*** 层序遍历的二叉树翻转** @param root* @return*/public TreeNode invertTree(TreeNode root) {// 参数校验if (root == null) {return null;}// 创建空间Queue<TreeNode> queue = new LinkedList<TreeNode>();// 根节点入队列queue.offer(root);// 只要队列不为空 就一直遍历while(!queue.isEmpty()){// 每次从队列中拿到一个节点,并交换这个节点的左右子树TreeNode temp = queue.poll();TreeNode left = temp.left;temp.left = temp.right;temp.right = left;// 如果当前节点的左子树不空  放入队列等待处理if (temp.left != null){queue.offer(temp.left);}// 如果当前节点的右子树不空  放入队列等待处理if (temp.right != null){queue.offer(temp.right);}}return root;}

总结

提示:二叉树的双指针问题;路径问题;翻转问题;回溯初始

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

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

相关文章

ros2学习笔记:shell环境变量脚本setup.bash[-z][-n][-f]参数作用

-n作用 [ -n 字符串 ] or [ 字符串 ] 字符串的长度为非零&#xff08;有内容&#xff09;则为真。加-n与不加-n结果相同。 -z作用 [ -z 字符串 ] 字符串的长度为零则为真。 字符串为空即NULL时为真&#xff0c;与上面的-n相反。 -f作用 [ -f FILE ] 如果 FILE 存在且是一…

【C语言】指针和数组笔试题解析(2)

【C语言】指针和数组笔试题解析&#xff08;1&#xff09;&#xff0c; 这是第一篇关于sizeof与strlen在指针中的应用&#xff0c;而这一篇主要讲解在各种情形下的灵活运用&#xff0c;也是大厂中经典的面试题 第一题&#xff1a; int main() {int a[5] { 1, 2, 3, 4, 5 };in…

uniapp 触底加载

方式一 onReachBottomDistance 缺点&#xff1a;需要整个页面滑动&#xff0c;局部滑动触发不了 { // pages.json // 路由下增加 onReachBottomDistance "path": "detailed/detailed","style": {"navigationBarTitleText": "收…

【无公网IP内网穿透】Windows搭建Web站点

什么是cpolar&#xff1f; cpolar是一个非常强大的内网穿透工具&#xff0c;开发调试的必备利器。 它可以将本地内网服务器的HTTP、HTTPS、TCP协议端口映射为公网地址端口&#xff0c;使得公网用户可以轻松访问您的内网服务器&#xff0c;无需部署至公网服务器。支持永久免费使…

idea 无法识别vue3语法

问题描述&#xff1a; 整合了vue3.js插件能够识别到vue标准语法和html语法第三方库的语法不支持 原因 因为没配置项目根路径&#xff0c;导致无法识别配置文件 而配置文件是识别第三方库语法所必须的。 我用vite操作的&#xff0c;需要这么几个包&#xff1a; 当识别到这…

22.(地图工具篇)geoserver点服务图层SLD样式效果1

地图之家总目录(订阅之前必须详细了解该博客) 效果如下: SLD样式 <?xml version="1.0" encoding="GBK"?> <StyledLayerDescriptor version="1.0.0" xsi:schemaLocation

【Linux网络编程】Socket-UDP实例

这份代码利用下面所有知识编写了一个简易聊天室&#xff08;基于Linux操作系统&#xff09;。虽然字数挺多其实并不复杂&#xff0c;这里如果能够看完或许会对你的知识进行一下串联&#xff0c;这篇文章比较杂并且网络编程这块知识需要用到系统编程的知识&#xff0c;希望能帮助…

Spring Boot集成EasyExcel实现数据导出

在本文中&#xff0c;我们将探讨如何使用Spring Boot集成EasyExcel库来实现数据导出功能。我们将学习如何通过EasyExcel库生成Excel文件&#xff0c;并实现一些高级功能&#xff0c;如支持列下拉和自定义单元格样式&#xff0c;自适应列宽、行高&#xff0c;动态表头 &#xff…

Open3D 点云配准——可视化匹配点对之间的连线

点云配准 一、算法原理1、概述2、主要函数二、代码实现三、结果展示四、测试数据本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫。 一、算法原理 1、概述 可视化源点云和目标点云中匹配点对之间的连线,这对于点云配准,尤…

【AI语言大模型】星火使用介绍

一、前言 现在AI语言大模型是百花齐放,挺好!有竞争,有发展,才能推出更好的产品。现在,科大讯飞就推出了大语言模型——星火!能够学习和理解人类的语言,进行多轮对话,回答问题,高效便捷地帮助人们获取信息、知识和灵感。星火在对话栏设置了三个插件:文档回答、PPT生成…

unity 使用声网(Agora)实现语音通话

第一步、先申请一个声网账号 [Agora官网链接]&#xff08;https://console.shengwang.cn/&#xff09; 第二步在官网创建项目 &#xff0c;选择无证书模式&#xff0c;证书模式需要tokenh和Appld才能通话 第三步 官网下载SDK 然后导入到unity&#xff0c;也可以直接在unity商店…

Linux界的老古董

Slackware 是由 Patrick Volkerding 制作的 Linux 发行版&#xff0c;从 1993 年发布至今也一直在 Patrick 带领下进行维护。7 月 17 日&#xff0c;Slackware 才刚刚过完它 24 岁的生日&#xff0c;看似年纪轻轻的它&#xff0c;已然是 Linux 最古老的发行版。 Slackware 的发…

第 363 场 LeetCode 周赛题解

A 计算 K 置位下标对应元素的和 模拟 class Solution { public:int pop_cnt(int x) {//求x的二进制表示中的1的位数int res 0;for (; x; x >> 1)if (x & 1)res;return res;}int sumIndicesWithKSetBits(vector<int> &nums, int k) {int res 0;for (int i…

Linux文件属性操作函数

1.access函数 #include <unistd.h> int access(const char *pathname, int mode); 作用&#xff1a;判断某个文件是否有某个权限&#xff0c;或者判断文件是否存在 参数: -pathname:判断的文件路径 -mode: R_OK&#xff1a;判断是否有读权限 W_OK X_OK F_OK&#xff1a;…

【海报生成器源码】设计海报生成器网站开源源码(更新)

源码简介: 随着社会经济和商业发展&#xff0c;对产品宣传的需求也加大了。如何快速制作海报也成了很大的需求。这里分享的是一个海报生成器网站的最新源代码。 这个海报编辑器有着实用强大的功能&#xff0c;它的最左侧是组件列表。可以在最左侧选择组件&#xff0c;比如文本…

华为云云耀云服务器L实例评测 | 开启OPC UA之旅

OPC Unified Architecture (OPC UA)是一种用于工业自动化的M2M协议(Machine-to-machine)&#xff0c;具有平台独立性&#xff0c;在Windows和Linux上都可以运行。随着云服务在工业现场的不断普及&#xff0c;OPCUA服务也开始大量部署在云端。 本文以华为云云耀云服务器L为基础…

3D目标检测数据集 KITTI(标签格式解析、点云转图像、点云转BEV)

本文介绍在3D目标检测中&#xff0c;理解和使用KITTI 数据集&#xff0c;包括KITTI 的基本情况、下载数据集、标签格式解析、点云转图像、点云转BEV。 目录 1、KITTI数据集中3D框可视化的效果 2、先看个视频&#xff0c;了解KITTI 的基本情况 3、来到KITTI官网&#xff0c;下…

计算机竞赛 机器视觉的试卷批改系统 - opencv python 视觉识别

文章目录 0 简介1 项目背景2 项目目的3 系统设计3.1 目标对象3.2 系统架构3.3 软件设计方案 4 图像预处理4.1 灰度二值化4.2 形态学处理4.3 算式提取4.4 倾斜校正4.5 字符分割 5 字符识别5.1 支持向量机原理5.2 基于SVM的字符识别5.3 SVM算法实现 6 算法测试7 系统实现8 最后 0…

Docker Swarm集群部署

Docker Swarm集群部署 任务平台 3台虚拟机&#xff0c;一台作为manager 节点&#xff0c;另两台作为work节点。 文章目录 Docker Swarm集群部署安装docker配置防火墙开放端口在 manager 节点创建 Swarm 集群创建用于swarm服务的自定义的overlay网络测试跨主机容器通信 安装do…

React使用useImperativeHandle实现父组件触发子组件事件

相关知识&#xff1a; useImperativeHandle forwardRef 相关代码&#xff1a; 获取子组件实例&#xff0c;由于这是函数组件&#xff0c;没有this因此不能整体获取&#xff0c;我们可以通过useImperativeHandle获取想要的变量或者方法。 父组件import React, { useRef } fro…