算法打卡day15|二叉树篇04|110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和

 算法题

Leetcode  110.平衡二叉树

题目链接:110.平衡二叉树

大佬视频讲解:平衡二叉树视频讲解

个人思路

可以用递归法,计算左右子树的高度差,当超过1时就不为平衡二叉树了;

解法

回顾一下二叉树节点的深度与高度;

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

在leetcode中深度从1开始的,有些地方从0开始;

递归法

类似于二叉树的后序遍历,即对于当前遍历到的节点:先递归地判断其左右子树是否平
衡,
再判断以当前节点为根的子树是否平衡。如果一棵子树是平衡的,则返回其高度(高度一定
是非负整数),否则返回 -1。如果存在一棵子树不平衡,则整个二叉树一定不平衡。

如果是用前序遍历,则对于同一个节点,函数 getHeight 会被重复调用,导致时间复杂
度较高。如果使用后序遍历的做法,则对于每个节点,函数 height 只会被调用一次。

也可以这样记忆,当树节点高度与深度不同时,求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)

class Solution {
    public boolean isBalanced(TreeNode root) {
        //1.确定递归函数参数以及返回值
        return getHeight(root) != -1;//判断是否为平衡二叉树
    }

    private int getHeight(TreeNode root) {
        
        if (root == null) {//根;前序遍历,先遍历根;
            return 0;
        }
        
        //3.确定单层递归逻辑
        int leftHeight = getHeight(root.left);//左
        if (leftHeight == -1) {
            return -1;
        }
        int rightHeight = getHeight(root.right);//右
        if (rightHeight == -1) {
            return -1;
        }
        // 左右子树高度差大于1,return -1表示已经不是平衡树了
        if (Math.abs(leftHeight - rightHeight) > 1) {//2.确定递归终止条件
            return -1;
        }

        return Math.max(leftHeight, rightHeight) + 1;
    }
}

时间复杂度:O(n);(遍历整棵树)

空间复杂度:O(n);(使用二叉树)

迭代法

来模拟后序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合,其中利用TreeNode.val来保存当前结点的高度,这样就不会有重复遍历;

class Solution {
    public boolean isBalanced(TreeNode root) {
        if (root == null) {
            return true;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode pre = null;
        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            TreeNode inNode = stack.peek();
            // 右结点为null或已经遍历过
            if (inNode.right == null || inNode.right == pre) {
                // 输出
                if (Math.abs(getHeight(inNode.left) - getHeight(inNode.right)) > 1) {
                    return false;
                }
                stack.pop();
                pre = inNode;
                root = null;// 当前结点下,没有要遍历的结点了
            } else {
                root = inNode.right;// 右结点还没遍历,遍历右结点
            }
        }
        return true;
    }

    // 求结点的高度
     
    public int getHeight(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = root.left != null ? root.left.val : 0;
        int rightHeight = root.right != null ? root.right.val : 0;
        int height = Math.max(leftHeight, rightHeight) + 1;
        root.val = height;// 用TreeNode.val来保存当前结点的高度
        return height;
    }
}

时间复杂度:O(n);(遍历整棵树)

空间复杂度:O(n);(一个栈的空间,最差情况下与树的空间一样,那就是n)


Leetcode 257. 二叉树的所有路径

题目链接:257. 二叉树的所有路径

大佬视频讲解:二叉树的所有路径视频讲解

个人思路

就知道好像是用回溯,但不记得怎么实现了

解法
递归法

要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径,因为要返回根到叶子结点的路径,所以肯定是需要回溯的,递归和回溯就是一家的;

因为是要记录到叶子节点的路径,所以左右孩子为空则为终止条件,到遍历到叶子节点,则将结果打印放入数组,然后回溯继续递归;

在写代码时,要记住回溯和递归是一一对应的,有一个递归,就要有一个回溯

class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        //1.确定递归函数参数以及返回值
        List<String> res = new ArrayList<>();// 存最终的结果
        if (root == null) {return res;}

        List<Integer> paths = new ArrayList<>();// 作为结果中的路径
        traversal(root, paths, res);
        return res;
    }

    private void traversal(TreeNode root, List<Integer> paths, List<String> res) {
        paths.add(root.val);// 前序遍历,中
        // 遇到叶子结点
        if (root.left == null && root.right == null) {//2.确定递归终止条件
            // 输出
            StringBuilder sb = new StringBuilder();// StringBuilder用来拼接字符串
            
            for (int i = 0; i < paths.size() - 1; i++) {
                sb.append(paths.get(i)).append("->");
            }
            
            sb.append(paths.get(paths.size() - 1));// 记录最后一个节点
            res.add(sb.toString());// 收集一个路径
            return;
        }
        
        //3.确定单层递归逻辑
        // 递归和回溯是同时进行,所以要放在同一个花括号里
        if (root.left != null) { // 左
            traversal(root.left, paths, res);//有一个递归,就要有一个回溯
            paths.remove(paths.size() - 1);// 回溯
        }
        if (root.right != null) { // 右
            traversal(root.right, paths, res);//有一个递归,就要有一个回溯
            paths.remove(paths.size() - 1);// 回溯
        }

    }
}

时间复杂度:O(n^2);(每遍历一条路径都要拷贝数据,高度乘节点,所以是n*n)

空间复杂度:O(n^2);(递归遍历暂存字符与结果,都需要空间)

迭代法

使用前序遍历的迭代方式来模拟遍历路径的过程


class Solution {

    public List<String> binaryTreePaths(TreeNode root) {
        List<String> result = new ArrayList<>();
        if (root == null)
            return result;

        Stack<Object> stack = new Stack<>();

        // 节点和路径同时入栈
        stack.push(root);
        stack.push(root.val + "");
        while (!stack.isEmpty()) {
            // 节点和路径同时出栈
            String path = (String) stack.pop();
            TreeNode node = (TreeNode) stack.pop();
            // 若找到叶子节点
            if (node.left == null && node.right == null) {
                result.add(path);
            }
            //右子节点不为空
            if (node.right != null) {
                stack.push(node.right);
                stack.push(path + "->" + node.right.val);
            }
            //左子节点不为空
            if (node.left != null) {
                stack.push(node.left);
                stack.push(path + "->" + node.left.val);
            }
        }

        return result;
    }
}

时间复杂度:O(n^2);(每遍历一条路径都要拷贝数据)

空间复杂度:O(n);(使用两个栈,一个存数据,一个遍历)


Leetcode 404.左叶子之和

题目链接:404.左叶子之和

大佬视频讲解:左叶子之和视频讲解

个人思路

可以用层序遍历,遍历到最后一层时,将左叶子筛选出来,结果值累加;

解法
递归法

递归的遍历顺序为后序遍历(左右中),是因为要通过递归函数的返回值来累加求取左叶子数值之和。

递归三部曲

1.确定递归函数的参数和返回值

判断一个树的左叶子节点之和,那么一定要传入树的根节点,递归函数的返回值为数值之和,所以为int

2.确定终止条件

如果遍历到空节点,那么左叶子值一定是0

注意,只有当前遍历的节点是父节点才能判断其子节点是不是左叶子。 所以如果当前遍历的节点是叶子节点,那其左叶子也必定是0:

3.确定单层递归的逻辑

当遇到左叶子节点的时候,记录数值,然后通过递归求取左子树左叶子之和,和 右子树左叶子之和,相加便是整个树的左叶子之和。

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if (root == null) return 0;
        int leftValue = sumOfLeftLeaves(root.left);    // 左
        int rightValue = sumOfLeftLeaves(root.right);  // 右
                                                       
        int midValue = 0;

        //判断是否为左叶子节点
        if (root.left != null && root.left.left == null && root.left.right == null) { 
             midValue = root.left.val;
        }
        int sum = midValue + leftValue + rightValue;  // 中
        return sum;
    }
}

时间复杂度:O(n);(遍历二叉树)

空间复杂度:O(n);(递归栈)

迭代法

层序遍历迭代法,采用前序遍历,在模板代码的基础上判断当前节点的子节点是否左叶子,并累加值;

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        int sum = 0;//左叶子节点和
        if (root == null) return 0;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            int size = queue.size();
            while (size -- > 0) {
                TreeNode node = queue.poll();
                //判断是否为左叶子节点
                if (node.left != null) { // 左节点不为空
                    queue.offer(node.left);
                    if (node.left.left == null && node.left.right == null){ // 左叶子节点
                        sum += node.left.val;
                    }
                }

                if (node.right != null) queue.offer(node.right);
            }
        }
        return sum;
    }
}

时间复杂度:O(n);(遍历二叉树)

空间复杂度:O(n);(使用队列)

以上是个人的思考反思与总结,若只想根据系列题刷,参考卡哥的网址代码随想录算法官网代码随想录算法官网代码随想录算法官网

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件测试知识面试题:白盒测试、黑盒测试、测试用例

文章目录 白盒测试1、白盒测试分两类2、白盒测试的四个原则3、白盒测试常用的7类测试 黑盒测试1、黑盒测试的优缺点2、黑盒测试的方法3、黑盒测试的原则 测试用例1、测试用例包含2、设计测试用例所需的文档资料3、采用白盒测试技术设计用例的目的4、采用黑盒测试技术设计用例的…

网络编程套接字(3)——Java数据报套接字(UDP协议)

目录 一、Java数据报套接字通信模型 二、UDP数据报套接字编程 1、DatagramSocket &#xff08;1&#xff09;DatagramSocket构造方法 &#xff08;2&#xff09;DatagramSocket方法 2、DatagramPacket &#xff08;1&#xff09;DatagramPacket构造方法 &#xff08;2&…

spring启动时如何自定义日志实现

一、现象 最近在编写传统的springmvc项目时&#xff0c;遇到了一个问题&#xff1a;虽然在项目的web.xml中指定了log4j的日志启动监听器Log4jServletContextListener&#xff0c;且开启了日志写入文件&#xff0c;但是日志文件中只记录业务代码中我们声明了日志记录器的日志&a…

HTML静态网页成品作业(HTML+CSS)——电影加勒比海盗介绍设计制作(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

数据结构从入门到精通——树和二叉树

树和二叉树 前言一、树概念及结构1.1树的概念1.2 树的相关概念&#xff08;重要&#xff09;1.3 树的表示1.4 树在实际中的运用&#xff08;表示文件系统的目录树结构&#xff09; 二、二叉树概念及结构2.1二叉树概念2.2现实中的二叉树2.3 特殊的二叉树2.4 二叉树的性质2.5 二叉…

PCB差分通孔的数值建模方法

目录 0 引言 1 基于CST的3D通孔模型 2 通孔模型的近似等效计算 3 利用ADS进行电路仿真分析 4 总结 0 引言 当数据速率超过10Gbps时&#xff0c;PCB上的通孔所带来的寄生参数会成为影响数据误码率的关键因素之一&#xff0c;虽然通过三维电磁场求解器提取过孔的行为模型&…

rust入门(1)创建项目

安装 vscode 安装插件 rust-analyzerNative Debug vscode 配置自动格式化代码 settings.json{"editor.defaultFoldingRangeProvider": null,"[rust]": {"editor.defaultFormatter": "rust-lang.rust-analyzer", // Makes the magi…

Python 井字棋游戏

井字棋是一种在3 * 3格子上进行的连珠游戏&#xff0c;又称井字游戏。井字棋的游戏有两名玩家&#xff0c;其中一个玩家画圈&#xff0c;另一个玩家画叉&#xff0c;轮流在3 * 3格子上画上自己的符号&#xff0c;最先在横向、纵向、或斜线方向连成一条线的人为胜利方。如图1所示…

静态时序分析:SDC约束命令set_output_delay详解

相关阅读 静态时序分析https://blog.csdn.net/weixin_45791458/category_12567571.html?spm1001.2014.3001.5482 目录 指定延迟值 指定端口、引脚列表 指定参考时钟 简单使用 指定时钟下降沿 指定参考端口、引脚 包含源、网络延迟 指定电平敏感 指定上升、下降沿 指…

Redux Toolkit

本文作者为 360 奇舞团前端开发工程师 阅读本文章前&#xff0c;需要先了解下 redux 的基本概念与用法&#xff0c;Redux Toolkit 是建立在 Redux 基础之上的工具包&#xff0c;因此需要对 Redux 的基本概念有一定的了解&#xff0c;包括 Action、Reducer、Store、Middleware 等…

C#四部曲(知识补充)

Unity跨平台原理 .Net相关 只要编写的时候遵循.NET的这些规则&#xff0c;就能在.NET平台下通用 各种源码→根据.NET规范编写→(虚拟机)生成CIL中间码(保存在程序集中)→转成操作系统原代码 跨语言← 跨平台↓ Unity跨平台原理&#xff08;Mono&#xff09; c#脚本→MonoC#编…

低压线性恒流LED恒流驱动芯片SM15633EH:用于洗墙灯和线条灯

洗墙灯和线条灯是两种常见的LED照明产品&#xff0c;它们都需要使用LED恒流驱动芯片来确保稳定、可靠的电流供应&#xff0c;从而保证LED的使用寿命和亮度。 对于洗墙灯而言&#xff0c;由于其发出的光线需要覆盖较大的区域&#xff0c;因此需要使用较大功率的LED芯片&#xf…

Linux操作系统与Windows文件互传(FTP)

一、开启Ubuntu下的FTP服务 打开Ubuntu的终端窗口&#xff0c;然后执行如下命令来安装 FTP服务。 sudo apt-get install vsftpd等待软件安装完成后&#xff0c;用输入以下命令打开vsftpd.conf文件 sudo vim /etc/vsftpd.conf 找到下图的两个使能语句改成如图即可(记住保存后再…

新版哥白尼系统快速下载哨兵数据

在新版哥白尼系统下载数据,总是failed或者速度很慢,如何实现MB/s的下载速度,只需要四步就可以解决: 1 登录系统,找到想下载的数据,点开product info按扭,​​​​​​ 2. 找到数据的Product id和Name, #Product id:https://zipper.dataspace.copernicus.eu/odata/v1/P…

Selenium操作浏览器,弹出文件选择框,实现自动选定“目标文件”

前言 本文是该专栏的第20篇,后面会持续分享python爬虫干货知识,记得关注。 我们在使用selenium操作目标页面的时候,可能会遇到如下图所示的情景。 在用selenium操作并点击页面元素的时候,会弹出一个文件选择框,需要我们选择目标文件,并点击确认按钮,目标文件才能上传成…

Stream-JDK8

Stream概念 代码示例 package com.zz.stream;import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors;/*** 使用Stream流* 找出姓张并且是三个字的名字*/ public class Test {public static void main(Stri…

cesiumlab白模效果一

效果 步骤 1、准备shp面数据 2、打开cesiumlab软件转换 选择shp面数据 设置高度&#xff0c;如果shp面中有高度字段&#xff0c;可以用高度字段&#xff0c;如果没有&#xff0c;可以用固定高度 设置贴图&#xff0c;这边用的是第二张效果&#xff0c;当然也可以用自己的贴图…

‘UnityEngine.Application‘ does not contain a definition for isBatchMode

unity 2017.4.37f1. 解决办法: Try to replace Application.isBatchMode with UnityEditorInternal.InternalEditorUtility.inBatchMode

MySQL实战:问题排查与监控

常见问题 有更合适的索引不走&#xff0c;怎么办&#xff1f; MySQL在选取索引时&#xff0c;会参考索引的基数&#xff0c;基数是MySQL估算的&#xff0c;反映这个字段有多少种取值&#xff0c;估算的策略为选取几个页算出取值的平均值&#xff0c;再乘以页数&#xff0c;即…

AI新工具(20240312) Midjourney官方发布角色一致性功能;免费且开源的简历制作工具;精确克隆语调、控制声音风格

1: Midjourney角色一致性功能 使人物画像在多方面高度一致成为可能。 Midjourney的角色一致性功能的使用方法如下&#xff1a; ⭐在你的输入指令后面加上 --cref URL&#xff0c;其中URL是你选择的角色图像的链接。 ⭐你可以通过 --cw 参数来调整参照的强度&#xff0c;范围…