代码随想录算法训练营第八天|二叉树(截止到左叶子之和)

news2024/11/19 14:37:10

翻转二叉树

Leecode 226.翻转二叉树

链接:https://leetcode.cn/problems/invert-binary-tree/

用递归来做,若是遇到空节点,直接return

然后交换左右节点,接着递归

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        swap(root->left, root->right);  // 中
        invertTree(root->left);         // 左
        invertTree(root->right);        // 右
        return root;
    }
};

对称二叉树

Leecode 101. 对称二叉树

链接:https://leetcode.cn/problems/symmetric-tree/

本来的想法是:用层序遍历的方法得到每层的vector后check其是不是回文,若是回文则对称,但是这种方法遇到示例2直接就死了

所以我们要换一种思路:不单单是记录每一层的节点,而是具体到左右节点去判断

例如当前如果左节点为空右节点不为空或者右节点为空左节点不为空,那么肯定是不符合题意的情况

先写出递归的四种终止条件

  1. 左空右不空 – false
  2. 左不空右空 – false
  3. 左空右空 – true
  4. 左不空右不空值不同 – false

然后我们在下面继续递归当前节点的左节点和右节点,别忘了用变量接住返回值

最后只有左子树和右子树的返回值都是true的时候才合法

 // 不要想层序遍历了好不好,二叉树的核心操作是递归啊!!!
// 递归的时候需要有值来接住返回值
class Solution {
public:
    bool check(TreeNode* l,TreeNode* r)
    {
        // 现在开始排除情况,若是左节点为空,右节点不空,或者是左节点空,右节点不空,那么绝对是不对称的
        if(l == NULL && r != NULL) return false;
        if(l != NULL && r == NULL) return false;
        if(l == NULL && r == NULL) return true;
        if(l -> val != r->val)     return false;
        bool left  =  check(l->left,r->right);
        bool right =  check(l->right,r->left);
        return left&&right;

    }
    bool isSymmetric(TreeNode* root) {
        if(root == NULL) return true;
        return check(root->left,root->right);
    }
};

二叉树的深度

Leecode104. 二叉树的最大深度

链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree/

我们要分清用递归实现前序遍历和后序遍历的区别

这里给出递归实现层序遍历的代码

class Solution {
public:
    void order(TreeNode* cur, vector<vector<int>>& result, int depth)
    {
        if (cur == nullptr) return;
        //---------------------------------------------------//
        if (result.size() == depth) result.push_back(vector<int>());
        result[depth].push_back(cur->val);
        //---------------------------------------------------//
        order(cur->left, result, depth + 1);
        order(cur->right, result, depth + 1);
    }
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> result;
        int depth = 0;
        order(root, result, depth);
        return result;
    }
};

关键代码已经用注释框出,也就是我们用递归“逐层下降”的时候,就用vector记录了每层的值,所以这是前序遍历

那么如何用递归实现后序遍历呢?

就是遇见NULL或者return的时候才记录值,此时记录到的值一定是从下往上的,因此是后序遍历

class Solution {
public:
    int getdepth(TreeNode *head,int depth)
    {
        if(head == NULL) return depth;
        int dep1 = getdepth(head -> left,depth+1);
        int dep2 = getdepth(head -> right,depth+1);

        return max(dep1,dep2);
    }
    int maxDepth(TreeNode* root) {
        int depth = 0;
        if(root == NULL) return 0;
        int maxx = getdepth(root,0);  // 刚开始传入的是一个结点吧,每次每次递归判断的都是一个结点
        return maxx;
    }
};

代码所示,就是后序遍历

Leecode 111. 二叉树的最小深度

链接:https://leetcode.cn/problems/minimum-depth-of-binary-tree/

首先我们要明白深度的定义:当前节点的深度就是当前节点到叶子节点的距离

我当时做这道题的时候直接就是改了求最大深度的代码中的max,将其改成min

class Solution {
public:
    int get_minn_dep(TreeNode *head,int depth)
    {
        // 这句话是不可以省略的,不然肯定会空指针
        // 我们已经知道:当左子树为空右子树不为空的时候,左子树的返回值不是最小值,同理右子树为空左子树不为空的时候右子树返回的也不是最小值
        // 那么我们在哪里处理这两种情况呢?——当前是当前节点不为空的时候才可以做判断
        // 那么什么时候才知道当前节点不为空呢?计算了l_dep和r_dep后面···
        if(head == NULL) return depth;
        int l_dep = get_minn_dep(head -> left, depth+1);
        int r_dep = get_minn_dep(head -> right,depth+1);
		
        // if(head -> left == NULL && head -> right!=NULL) l_dep = r_dep;
        // if(head -> left != NULL && head -> right==NULL) r_dep = l_dep;
        return min(l_dep,r_dep);
    }
    int minDepth(TreeNode* root) {
        if(root == NULL) return 0;
        int res = get_minn_dep(root,0);
        return res;
    }
};

在这里插入图片描述

结果发现我们样例都过不了,仔细分析,发现对于这种形状的树(每个节点只有右儿子),按照上面代码来跑,结果是1而不是5

显然root节点的左儿子为空,所以返回的最小深度是1,这肯定是错误的,所以我们还要加上两句(也就是注释了的两句):

若左节点为空,而右节点不为空,得到的左节点的深度就等于右节点的深度

若右节点为空,而左节点不为空,得到的右节点的深度就等于左节点的深度

完全二叉树

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

链接:https://leetcode.cn/problems/count-complete-tree-nodes/

可以用对付普通二叉树的方法做,也可以利用完全二叉树的性质做

// 普通方式
class Solution {
public:
    int cal(TreeNode *head)
    {
        if(head == NULL) return 0;
        int l_sum = cal(head->left);
        int r_sum = cal(head->right);

        return 1 + l_sum + r_sum;
    }
    int countNodes(TreeNode* root) {
        if(root == NULL) return 0;
        int num = cal(root);
        return num; 
    }
};

那么完全二叉树的性质是什么?

左子树的深度 = 右子树的深度

若是满足这个性质,那么整棵树的节点数量(包含根节点)就是(2 << 深度) - 1

// 利用完全二叉树的性质
class Solution {
public:
    int countNodes(TreeNode* root) {
        // 如果是空的话直接返回
        if(root == NULL) return 0;
        // 然后我们分别定义出左节点和右节点
        TreeNode* left  = root->left;
        TreeNode* right = root->right;

        int l_num = 0;
        int r_num = 0;

        while(left!= NULL)
        {
            left  = left->left;
            l_num ++;
        } 

        while(right!= NULL)
        {
            right  = right->right;
            r_num ++;
        } 

        if(l_num == r_num) return ((2 << l_num) - 1); // 如果当前子树有三层,那么一共是7个节点

        return countNodes(root -> left) + countNodes(root -> right) + 1;
    }
};

平衡二叉树

Leecode 110. 平衡二叉树

首先看平衡二叉树的定义:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1

显然,如果我们再遍历树的时候,遇到了不符合题意的情况,就要直接返回-1

之前做的题目都是累加深度,这是值得注意的一点

那么若是符合平衡二叉树呢?父节点的深度还要累加子节点的深度

所以正常满足平衡二叉树的定义时,我们就要返回当前节点的最大深度

接下来我们思考递归怎么写?

函数的返回值是int

若是空节点,我们就返回0

下面我们分别遍历左右子树,并用变量接住返回值

若是左右子树中有一个返回-1,那么最后返回的就是-1

否则,我们返回左右子树的最大深度+1

class Solution {
public:
    int check(TreeNode *head)
    {
        if(head == NULL) return 0;
        int l_num = check(head -> left);
        int r_num = check(head -> right);
        // 那如果左右都是-1呢?那不就寄了吗,所以左右返回值还需要判断,如果左右返回值有一个是-1,那么直接返回-1
        if(l_num == -1 || r_num == -1) return -1;
        if(abs(l_num - r_num) > 1) return -1;
        return max(l_num,r_num) + 1;

        // 返回值是什么,若是满足平衡二叉树,那么就返回当前节点的深度,若不是平衡二叉树,那么就返回-1
    }
    bool isBalanced(TreeNode* root) {
        if(root == NULL) return true;
        int res = check(root);
        if(res == -1) return false;
        return true;
    }
};

二叉树的所有路径

Leecode 257. 二叉树的所有路径

链接:https://leetcode.cn/problems/binary-tree-paths/
在这里插入图片描述

拿上图举例:我们不仅要输出[1->2->5],还要输出[1->3]

显然根节点出现了两次,显然是需要回溯操作的

那么什么时候回溯呢?取决于我们记录到哪些元素,不像之前我们遇到了空节点才return,因为我们要记录所有节点,而且还是从上到下记录,因此我们不能return后再记录,而是向下遍历一层记录一次,也就是前序遍历。那么可以遍历到空节点吗?显然不行,因此return条件我们需要改一下,将其改成——遇到子节点(左右节点都为空)的时候才return,return之前,我们记录当前vector中记录到的所有节点并将其生成一条路径

返回条件改变,自然我们遍历的时候也不能随意指向,一定要是保证左右儿子存在的时候才往下遍历

那么回溯操作体现在哪里呢?自然是上面函数return了之后,当前函数的执行位置就是我们“往下遍历”的语句下面,我们需要回溯

孙哥给了一种写法,为了让回溯更明显,我们一直都是引用记录数字的vector,因此我们需要回溯的时候直接pop_back()记录数组的vector即可

下面看代码

// 递归 + 回溯如何实现前序遍历?
// 每次往下遍历的时候都加入当前元素就OK
// 因为我们要加入的是全部路径,所以末尾的空元素我们是不需要的,所以递归的结束条件就是“左右孩子都为空”
class Solution {
public:
    void search(TreeNode *root,vector<int>& num,vector<string> &res) // 如果不对num加上引用,那么就只输出一次头结点
    {
        num.push_back(root->val);

        if(root -> left == NULL && root -> right == NULL)
        {
            string path;
            for(int i=0;i<num.size()-1;i++)
            {
                path += to_string(num[i]);
                path += "->";
            } // 因为最后一个节点的最后是没有“->”的,所以单独取出
            path += to_string(num[num.size()-1]);
            res.push_back(path); 
            return;
            // 这里没有return,太危险了!!!!!
        }

        // 然后向左遍历,注意要回溯
        if(root -> left!=NULL)  
        {
            search(root->left ,num,res);
            num.pop_back(); // vector中用pop_back去回溯
        }
        // 然后向右遍历
        if(root -> right!=NULL)
        {
            search(root->right ,num,res);
            num.pop_back();
        }
    }
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> res;
        vector<int> number;
        if(root == NULL) return res;
        search(root,number,res); // 没有返回值,直接全都加入到res数组中
        return res; 
    }
};

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

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

相关文章

Java代码审计——SSH 框架审计技巧

目录 &#xff08;一&#xff09; SSH 框架简介 &#xff08;二&#xff09; Java SSH 框架审计技巧 &#xff08;一&#xff09; SSH 框架简介 上个月介绍了 SSM 框架&#xff0c;即 Spring MVC、Spring 和 MyBatis。接下来介绍 Java Web曾经开发的 SSH 框架&#xff0c;即 …

河北涿州水稻种植历史 国稻种芯·中国水稻节:保定效益双赢

河北涿州水稻种植历史 国稻种芯中国水稻节&#xff1a;保定效益双赢 央视网消息 保定日报讯&#xff08;通讯员张千 刘永兴 王蕾&#xff09;新闻中国采编网 中国新闻采编网 谋定研究中国智库网 中国农民丰收节国际贸易促进会 国稻种芯中国水稻节 中国三农智库网-功能性农业农…

浅谈前缀索引

一.什么是前缀索引 所谓前缀索引说白了就是对字符串或前n个字符建立索引 二.为什么选择前缀索引 一般来说使用前缀索引&#xff0c;可能都是因为整个字段的数据量太大&#xff0c;没有必要针对整个字段建立索引&#xff0c;前缀索引仅仅是选择一个字段的前n个字符作为索引&a…

Linux运维工程师的操作规范

&#xff0c;Linux运维工程师的操作规范从事运维有一段时间了&#xff0c;遇到过各式各样的问题&#xff0c;数据丢失&#xff0c;网站挂马&#xff0c;误删数据库文件&#xff0c;黑客攻击等各类问题。 今天简单整理一下&#xff0c;分享给各位小伙伴。 一、线上操作规范 1、…

你可见过如此细致的延时任务详解

概述 延时任务相信大家都不陌生&#xff0c;在现实的业务中应用场景可以说是比比皆是。例如订单下单 15 分钟未支付直接取消&#xff0c;外卖超时自动赔付等等。这些情况下&#xff0c;我们该怎么设计我们的服务的实现呢&#xff1f; 笨一点的方法自然是定时任务去数据库进行轮…

华为机试 - 滑动窗口最大和

目录 题目描述 输入描述 输出描述 用例 题目解析 算法源码 题目描述 有一个N个整数的数组&#xff0c;和一个长度为M的窗口&#xff0c;窗口从数组内的第一个数开始滑动直到窗口不能滑动为止&#xff0c; 每次窗口滑动产生一个窗口和&#xff08;窗口内所有数的和&…

常用的框架技术-09 Spring Security Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录1.Spring Security简介1.1 Spring Security概述1.2 Spring Security历史发展1.3 产品的对比1.3.1 Spring Security1.3.2 Shiro1.4 Spring Security 核心类1.4.1 Auth…

qemu 线程 vhost

[rootlocalhost cloud_images]# lsmod | grep vhost_net vhost_net 262144 0 vhost 262144 1 vhost_net tap 262144 1 vhost_net tun 262144 2 vhost_net [rootlocalhost cloud_images]#vhost-net网卡的…

[附源码]SSM计算机毕业设计基于实时定位的超市配送业务管理JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

低碳世界杂志低碳世界杂志社低碳世界编辑部2022年第7期目录

节能环保 挥发性有机物的全厂控制措施 董少军; 1-3 《低碳世界》投稿&#xff1a;cnqikantg126.com 佛山市市政排水管网通沟污泥处理处置工艺设计 张红; 4-6 “双碳”背景下海岸带地区适应气候变化评估与对策研究 王鸿浩;邬乐雅;吴晓晨;张丽佳;黄婧蓼琦;胡斐…

【毕业设计】基于情感分析的网络舆情热点分析系统

文章目录0 前言1 课题背景2 数据处理3 文本情感分析3.1 情感分析-词库搭建3.2 文本情感分析实现3.3 建立情感倾向性分析模型4 数据可视化工具4.1 django框架介绍4.2 ECharts5 Django使用echarts进行可视化展示5.1 修改setting.py连接mysql数据库5.2 导入数据5.3 使用echarts可视…

Java编程实战9:统计只差一个字符的子串数目

目录统计只差一个字符的子串数目题目示例 1示例 2示例 3示例 4提示解答解题思路完整代码统计只差一个字符的子串数目 题目 给你两个字符串 s 和 t &#xff0c;请你找出 s 中的非空子串的数目&#xff0c;这些子串满足替换 一个不同字符 以后&#xff0c;是 t 串的子串。换言…

实验1:Arduino的nRF24L01单向收发实验

实验结果: 发送端发送“Hello World”,发送成功打印1 接收端接收到“Hello World”,在串口中打印出“Hello World” OK,直接讲代码 因为我用的Arduino和nRF24L01 是用扩展板连接的,而我的嵌入式硬件开发,也就是AD实在不擅长,就不解释了 其中(9,10)CE,CSN 那么我…

通关算法题之 ⌈数组⌋ 下

二分搜索 704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 输入: nums [-1,0,3,5,9,12], target 9 输出…

【后台技术】异步编程指北,问题和重点

导语&#xff1a;同步、异步&#xff0c;并发、并行、串行&#xff0c;这些名词在我们的开发中会经常遇到&#xff0c;这里对异步编程做一个详细的归纳总结&#xff0c;希望可以对这方面的开发有一些帮助。 内容大纲&#xff1a; 1、几个名词的概念 多任务的时候&#xff0c;…

jmeter压力测试报告

出版社智能智造测试报告 &#xff08;二期版本&#xff09; 2022年11月 目 录 1. 测试背景 1.1. 项目背景 1.2. 测试目的 1.3. 测试时间 1.4. 测试资源 1.5. 参考资料 2. 测试范围 3. 性能需求指标 3.1. 业界指标 4. 测试工具 5. 测试环境 5.1. 阿里云测试环境软…

搭建Gitlab

Gitlab是目前被广泛使用的基于git的开源代码管理平台, 基于Ruby on Rails构建, 主要针对软件开发过程中产生的代码和文档进行管理 一、搭建gitlab服务器&#xff0c;统一管理软件项目 第一步&#xff1a; 创建一个4G内存的虚拟机&#xff0c;否则很容易启动不了&#xff0c;报…

(附源码)计算机毕业设计Java“华商转转”平台的设计和实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…

python常用进制转换

整数之间转换 # 1. 10 -> 16 hex(number)# 2. 10 -> 2 bin(number)# 3. 10 -> 8 oct(number)# 4. x进制 -> 10 int(Union[str, bytes, bytearray],basex) ------------------ print(int("0x16", base16)) // 22字符串转整数 # 10进制 val int(10) pri…

SPP-学习笔记

Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition SPP提出的原因 1、现有的深度卷积神经网络(spp出现之前的)需要固定大小的输入图像(例如224224)。往往需要对图片裁剪或者resize&#xff0c;导致图片信息损失或者产生几何畸变。这样可能会损…