Day20 222完全二叉树的节点个数 110平衡二叉树 257二叉树的所有路径

news2025/1/17 3:41:30

222 完全二叉树的结点个数

        本题先不把它当成完全二叉树来看,用广度优先和深度优先搜索分别遍历,也能达到目的,只要将之前的代码稍加修改即可。注意后序遍历时的result要加上自身本身的那个结点。

//后序递归遍历
class Solution {
public:
    int countNodes(TreeNode* root) {
        if(root==nullptr) return 0;
        int leftnum = countNodes(root->left);
        int rightnum = countNodes(root->right);
        int result = leftnum + rightnum +1;
        return result;
        }
};
//层序遍历
class Solution {
public:
    int countNodes(TreeNode* root) {
        queue<TreeNode*> que;
        int result = 0;
        if(root != NULL)
            que.push(root);
        while(!que.empty())
        {
            int size = que.size();
            while(size--)
            {
                TreeNode* node = que.front();
                que.pop();
                result++;
                if(node->left)
                    que.push(node->left);
                if(node->right)
                    que.push(node->right);
            }
        }
        return result;
    }
};

        既然本题给出了条件完全二叉树,那就可以用完全二叉树的方法来做。可以继续使用递归法,终止条件要稍微变一下,要根据左右深度是否相同来判断子树是不是满二叉树,如果是的话就返回2的n次方-1,不是的话就继续递归。为什么左右深度相等就一定是满二叉树呢,这取决于完全二叉树的特性,也就是最后那层肯定是连续的,不存在中间为空的情况。

class Solution {
public:
    int countNodes(TreeNode* root) {
        if (root == nullptr) return 0;
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便
        while (left) {  // 求左子树深度
            left = left->left;
            leftDepth++;
        }
        while (right) { // 求右子树深度
            right = right->right;
            rightDepth++;
        }
        if (leftDepth == rightDepth) {
            return (2 << leftDepth) - 1; // 注意(2<<1) 相当于2^2,所以leftDepth初始为0
        }
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

 110 平衡二叉树

递归:

        要求高度,首先想到要后序遍历,终止条件为遍历到空节点,单层递归逻辑为:分别求出本节点左右子树的高度,如果高度等于-1,就说明左右子树之一不平衡,如果均不为-1, 就继续往下进行,比较左右子树的高度差,大于1说明有问题,返回-1,其余情况正常算高度。

class Solution {
public:
    int getHeight(TreeNode* node){
    if(node == nullptr) return 0; //终止条件
    int leftHeight = getHeight(node->left);
    if(leftHeight==-1) return -1;
    int rightHeight = getHeight(node->right);
    if(rightHeight == -1) return -1;
    return (abs(leftHeight-rightHeight) > 1 ? -1 : (1+max(leftHeight,rightHeight)));}

    bool isBalanced(TreeNode* root) {
        return (getHeight(root)==-1 ? false : true);
    }
};

迭代:

        本题也可以通过迭代法来进行遍历,但是不能直接用层序遍历来求高度,这就体现出求高度和求深度的不同之处。本题的迭代方式可以先定义一个函数专门求高度。

class Solution {
public:
    int getDepth(TreeNode*node)
    {
        queue<TreeNode*> que;
        if(node!=nullptr) que.push(node);
        int depth = 0;
        while(!que.empty())
        {
            int size = que.size();
            depth++;
            while(size--)
            {
                TreeNode* cur = que.front();
                que.pop();
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
        }
        return depth;
    }
    
    bool isBalanced(TreeNode* root) {
        queue<TreeNode*> que;
        if(root == nullptr) return true;
        else que.push(root);
        while(!que.empty())
        {
            int size = que.size();
            while(size--)
            {
                TreeNode*node=que.front();
                que.pop();
                if(abs(getDepth(node->left)-getDepth(node->right))>1)
                    return false;
                if(node->left) que.push(node->left);
                if(node->right) que.push(node->right);
            }
        }
        return true;
    }
};

257 二叉树的所有路径  

         本题很明显要进行前序遍历,才方便让父亲节点指向孩子节点,找到对应的路径。但是既然要输出所有的路径,我觉得本题是时候该考虑回溯了。

         首先是递归法:1.参数返回值:传入根节点,记录每一条路径的path,存放结果的result。

2.递归终止条件。在写递归时都习惯了写cur==null终止,但是本题如果这么写会比较麻烦,因为本题是要找到叶子节点,就结束把路径放进result里的处理了。那么为什么要用vector<int> path来记录路径呢,因为单层递归的逻辑时,要做回溯,使用vector方便回溯过程。3.确定单层递归逻辑,因为前序遍历,所以先处理中间结点,之后就是递归和回溯的过程。

        要注意:回溯要和递归永远在一起,世界上最遥远的距离是你在花括号里,而我在花括号外!有一个递归,就要对应一个回溯!

        整体代码如下:

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;
    }
};

         每次回溯都要pop出此时容器里存放的那个数字,这样就能找出不同的路径了。为什么要用引用呢因为既然每次都pop了,所以说这个path每次都是在变的。

可以精简成如下代码:

class Solution {
private:

    void traversal(TreeNode* cur, string path, vector<string>& result) {
        path += to_string(cur->val); // 中
        if (cur->left == NULL && cur->right == NULL) {
            result.push_back(path);
            return;
        }
        if (cur->left) traversal(cur->left, path + "->", result); // 左
        if (cur->right) traversal(cur->right, path + "->", result); // 右
    }

public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> result;
        string path;
        if (root == NULL) return result;
        traversal(root, path, result);
        return result;

    }
};

         这个代码里也有递归回溯,因为传入的这个string并不是引用形式,每次执行完毕以后,返回到上一个都不会让这次的影响对上一次进行改变,所以-》要写在递归函数里面,如果写成:

path += "->";
traversal(cur->left, path, result); // 左

         path就会被改变以后再进行递归,无法进行回溯,这回导致-》重复。如果非要这么写,就得用第一种方法把他pop出去:

if (cur->left) {
    path += "->";
    traversal(cur->left, path, result); // 左
    path.pop_back(); // 回溯 '>'
    path.pop_back(); // 回溯 '-'
}
if (cur->right) {
    path += "->";
    traversal(cur->right, path, result); // 右
    path.pop_back(); // 回溯 '>' 
    path.pop_back(); //  回溯 '-' 
}

         本题也可以采用非递归的方式,类似于前序遍历的迭代法,不过要定义两个栈,一个是正常迭代法里的那个遍历栈,另一个是保存每次路径的栈:

class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        stack<TreeNode*> treeSt;// 保存树的遍历节点
        stack<string> pathSt;   // 保存遍历路径的节点
        vector<string> result;  // 保存最终路径集合
        if (root == NULL) return result;
        treeSt.push(root);
        pathSt.push(to_string(root->val));
        while (!treeSt.empty()) {
            TreeNode* node = treeSt.top(); treeSt.pop(); // 取出节点 中
            string path = pathSt.top();pathSt.pop();    // 取出该节点对应的路径
            if (node->left == NULL && node->right == NULL) { // 遇到叶子节点
                result.push_back(path);
            }
            if (node->right) { // 右
                treeSt.push(node->right);
                pathSt.push(path + "->" + to_string(node->right->val));
            }
            if (node->left) { // 左
                treeSt.push(node->left);
                pathSt.push(path + "->" + to_string(node->left->val));
            }
        }
        return result;
    }
};

         这两个栈是紧密联系的,上面动的时候,下面也会跟着动。这里的treeSt肯定是只遍历一遍的,但是pathSt里面存的是一个路径,上次遍历的部分会依旧保留,记录了之前遍历过的部分。

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

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

相关文章

CGAL的D维包围盒相交计算

包围盒相交测试是一种用于快速判断两个三维对象是否相交的方法&#xff0c;而AABB树则是一种数据结构&#xff0c;常用于加速场景中的射线检测和碰撞检测。 首先&#xff0c;让我们了解一下包围盒相交测试。这种测试的目的是为了快速判断两个三维对象是否相交&#xff0c;而不需…

数据仓库 基本信息

数据仓库基本理论 数据仓库&#xff08;英语&#xff1a;Data Warehouse&#xff0c;简称数仓、DW&#xff09;,是一个用于存储、分析、报告的数据系统。数据仓库的目的是构建面向分析的集成化数据环境&#xff0c;为企业提供决策支持&#xff08;Decision Support&#xff09…

【轻松入门】OpenCV4.8 + QT5.x开发环境搭建

引言 大家好&#xff0c;今天给大家分享一下最新版本OpenCV4.8 QT5 如何一起配置&#xff0c;完成环境搭建的。 下载OpenCV4.8并解压缩 软件版本支持 CMake3.13 或者以上版本 https://cmake.org/ VS2017专业版或者以上版本 QT5.15.2 OpenCV4.8源码包 https://github.com/op…

主浏览器优化之路1——你现在在用的是什么浏览器?Edge?谷歌?火狐?360!?

上一世&#xff0c;我的浏览器之路 引言为什么要用两个浏览器为什么一定要放弃火狐结尾给大家一个猜数字小游戏&#xff08;测运气&#xff09; 引言 小时候&#xff0c;我一开始上网的浏览器是2345王牌浏览器吧&#xff0c; 因为上面集成了很多网站&#xff0c;我记得上面有7…

【MySQL】多表连接查询

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; 数 据 库 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 1. 交叉连接&#xff08;CROSS JOIN&#xff09; 2. 内连接&#xff08;INNER JOIN&#xff09; 3. 外连接 结语 我的…

几款软件助您事半功倍

在如今繁忙而竞争激烈的工作环境中&#xff0c;寻找适合自己的工作软件是提高工作效率、优化工作流程的重要一环。为了帮助你更好地管理任务、组织工作和提高生产力&#xff0c;我将向你推荐四款备受推崇的工作软件&#xff0c;并详细介绍它们各自的功能和特点。 1. Zoom&#…

记录使用minikube部署web程序,并灰度发布不同版本

1. 安装软件 1.1安装docker desktop 下载地址 重点&#xff1a;配置镜像加速 1.2 安装k8s&minikube 这里参考阿里社区的配置 minikube1.24.0版本下载地址 重点&#xff1a;安装版本问题【因为后面要用阿里云的服务来获取所需Docker镜像&#xff0c;一直不成功使用的高版…

软件测试/测试开发丨Pytest学习笔记

Pytest 格式要求 文件: 以 test_ 开头或以 _test 结尾类: 以 Test 开头方法/函数: 以 _test 开头测试类中不可以添加构造函数, 若添加构造函数将导致Pytest无法识别类下的测试方法 断言 与Unittest不同, 在Pytest中我们需要使用python自带的 assert 关键字进行断言 assert…

JOSEF约瑟 双位置继电器 DCS-12/110V 线圈电压直流110V 板前安装

系列型号&#xff1a; DCS-11双位置继电器&#xff1b; DCS-12双位置继电器&#xff1b; DCS-13双位置继电器&#xff1b; RXMVB2 RK 251 204双位置继电器&#xff1b; RXMVB2 RK 251 205双位置继电器&#xff1b; RXMVB2 RK 251 106双位置继电器&#xff1b; 一、用途 …

Flink项目实战篇 基于Flink的城市交通监控平台(下)

系列文章目录 Flink项目实战篇 基于Flink的城市交通监控平台&#xff08;上&#xff09; Flink项目实战篇 基于Flink的城市交通监控平台&#xff08;下&#xff09; 文章目录 系列文章目录4. 智能实时报警4.1 实时套牌分析4.2 实时危险驾驶分析4.3 出警分析4.4 违法车辆轨迹跟…

6.Nacos

1.单机部署 1.1 官网 https://nacos.io/zh-cn/index.html https://github.com/alibaba/Nacos 1.2.版本说明 https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E 1.3.下载地址 https://github.com/alibaba/nacos/releases/tag/2.2.…

百度CTO王海峰:飞桨开发者已达1070万

目录 写在前面 飞桨开发者已达1070万 文心一言用户规模破亿&#xff0c;日提问量快速增长 写在前面 “文心一言用户规模突破1亿。”12月28日&#xff0c;百度首席技术官、深度学习技术及应用国家工程研究中心主任王海峰在第十届WAVE SUMMIT深度学习开发者大会上宣布。会上&…

全平台去水印系统源码:画质高清无损害,一键下载 支持目前主流80多个平台无水印下载 带完整的安装部署教程

在数字内容爆炸的时代&#xff0c;图片和视频的传播和使用越来越频繁。然而&#xff0c;许多优质资源都带有水印&#xff0c;不仅影响了美观&#xff0c;也在一定程度上限制了资源的再利用。传统的去水印方法往往操作复杂&#xff0c;效果不尽如人意&#xff0c;甚至可能损害原…

《网络是怎样连接的》1.2、1.3节图表(自用)

图2.1&#xff1a;浏览器调用socket库中的解析器&#xff0c;向DNS服务器询问域名的ip地址 &#xff08;图中的gethostbyname是解析器的名称&#xff1b;协议栈是操作系统的网络控制软件&#xff0c;也称协议驱动、TCP/IP驱动&#xff09; 图2.2 DNS服务器根据客户端查询信息查…

Vue2.0 -- 组件局部注册

目录 组件定义 注册 使用组件 组件的命名 再做vue之前, 需要先引入vue.js文件 <script src"../js/vue.js"></script>有很多官方或者非官方的cdn可以使用, 可自行前往 搜索下载 组件定义 首先, 使用Vue.extend() 来定义一个组件 (注意这个步骤是在sc…

mac 生成 本地.ssh

输入下面命令行 ssh-keygen 默认回车得到下面的 Generating public/private rsa key pair. Enter file in which to save the key (/Users/{用户名}/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has be…

【 FPGA 封装设计资源 】 Xilinx vs Altera

XILINX PACKAGE 一般在doc nav搜索&#xff0c;同样也可以在官网&#xff1b;检索关键字“*pkg-pinout.” 比如vu9p: ug575-ultrascale-pkg-pinout.pdf 原理库文件 Package Files Portal 举例&#xff1a; 先选封装&#xff1b; 再选器件 二维交叉检索后&#xff0c;在右击…

【力扣题解】P404-左叶子之和-Java题解

&#x1f468;‍&#x1f4bb;博客主页&#xff1a;花无缺 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 花无缺 原创 收录于专栏 【力扣题解】 文章目录 【力扣题解】P404-左叶子之和-Java题解&#x1f30f;题目描述&#x1f4a1;题解&#x1f30f;总结…

配置inotify+rsync实时同步

Linux内核从2.6.13版本开始提供了inotify通知接口&#xff0c;用来监控文件系统的各种变化情况&#xff0c;如 文件存取&#xff0c;删除、移动&#xff0c;修改等&#xff0c;利用这一机制&#xff0c;可以非常方便地实现文件异动告警、增量备份&#xff0c; 并针对目录或文件…

main函数的参数ac和av

概要&#xff1a; main函数有两个参数&#xff0c;ac和av ac表示参数的个数&#xff0c;程序名包括在内。也就是说程序无参数运行时&#xff0c;ac的值为1 av是一个字符串数组&#xff0c;这个数组中的每个元素表示一个参数&#xff0c;程序名包括在内。也就是说&#xff0c…