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

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里面存的是一个路径,上次遍历的部分会依旧保留,记录了之前遍历过的部分。

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

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

相关文章

【ARMv8M Cortex-M33 系列 2 -- Cortex-M33 JLink 连接 及 JFlash 烧写介绍】

文章目录 Jlink 工具JLink 命令行示例JFlash 烧写问题Jlink 工具 J-Link 是 SEGGER 提供的一款流行的 JTAG 调试器,它支持多个平台和处理器。JLink.exe 是 J-Link 调试器的命令行接口,它允许用户通过命令行执行一系列操作,例如编程、擦除、调试等。 工具链接: https://ww…

C#上位机与欧姆龙PLC的通信07----使用第3方通讯库读写数据

1、介绍 FINS (factory interface network service)通信协议是欧姆龙公司开发的用于工业自动化控制网络的指令/响应系统。运用FINS指令可实现各种网络间的无缝通信&#xff0c;通过编程发送FINS指令&#xff0c;上位机或PLC就能够读写另一个PLC数据区的内容&#xff0c;甚至控…

Python sanic框架钉钉和第三方打卡机实现

同样还是需要开通钉钉应用这里就不错多说了 第一步:梳理逻辑流程 前提&#xff1a;打卡的机器是使用postgres数据库&#xff0c;由于因为某些原因&#xff0c;钉钉userId 我已经提前获取到了存放到数据库里。 1.用户打卡成功后&#xff0c;我们应该监听数据库进行查询&#xf…

西北大学844计算机类考研-25级初试高分总攻略

西北大学844计算机类考研-25级初试高分攻略 个人介绍 ​ 本人是西北大学22级软件工程研究生&#xff0c;考研专业课129分&#xff0c;过去一年里在各大辅导机构任职&#xff0c;辅导考研学生专业课844&#xff0c;辅导总时长达400小时&#xff0c;辅导学生超过20余人&#xf…

鸿蒙(HarmonyOS)项目方舟框架(ArkUI)之线性布局容器Row组件

鸿蒙&#xff08;HarmonyOS&#xff09;项目方舟框架&#xff08;ArkUI&#xff09;之线性布局容器Row组件 一、操作环境 操作系统: Windows 10 专业版、IDE:DevEco Studio 3.1、SDK:HarmonyOS 3.1 二、Row组件 沿水平方向布局容器。 子组件 可以包含子组件。 接口 Row(…

【SVN】Windows版合并提交bat文件+自定义菜单快捷键

【工具向】利用bat批处理打开TortoiseGit简化版本管理流程_tortoisegit bat-CSDN博客 start cmd /k "cd C:\YourBranchProj && svn cleanup && svn update && svn merge C:\YourTrunkProj -r 历史版本号:HEAD && svn commit -m "me…

pyCharm 打印控制台中文乱码解决办法

解决方法 在 "File" -> "Settings" 中的控制台设置&#xff1a; 在 "File" -> "Settings" 中&#xff0c;你可以找到 "Editor" -> "General" -> "Console"。在这里&#xff0c;你可能会找到…

数据结构:栈

1.栈的定义 栈是仅限在表尾进行插入和删除的线性表&#xff0c;栈又被称为后进先出的线性表 1.1栈顶和栈底 栈是一个线性表&#xff0c;我们允许插入和删除的一端称为栈顶 栈底和栈顶相对&#xff0c;实际上栈底的元素不需要关心 1.2入栈和出栈 栈元素的插入操作叫做入栈&…

“2023年的技术发展与个人成长:回顾与展望“

文章目录 每日一句正能量前言工作生活未来展望后记 每日一句正能量 凡事顺其自然&#xff0c;遇事处于泰然&#xff0c;得意之时淡然&#xff0c;失意之时坦然&#xff0c;艰辛曲折必然&#xff0c;历尽沧桑悟然。 前言 在这快速发展的信息时代&#xff0c;技术的进步和创新不…

vue2中使用百度地图BMapGL

1、npm 命令安装 npm install vue-bmap-gl --save2、main.js 中文件引入 import VueBMap from vue-bmap-gl import vue-bmap-gl/dist/style.css VueBMap.initBMapApiLoader({// 百度的keyak:*********,// 这个密钥请使用自己注册的 }) Vue.use(VueBMap)3、页面调用 <temp…

Python 中的数学运算(Python Math)

Python中的math模块是数学运算的重要工具&#xff0c;提供了丰富的数学函数和常数。本文将深入探讨math模块的功能和用法&#xff0c;使您能够更好地利用Python进行数学运算。 Python的math模块是一个强大的工具集&#xff0c;涵盖了许多基本的数学函数和常数&#xff0c;适用…

C++11 lambda函数和包装器

目录 前言 一.lambda的引入 二、lambda函数的使用 1.一般使用 2.引用 三、包装器 1.包装普通对象 2.包装类成员对象 3.bind 前言 学习过python的同学应该对lambda函数不陌生&#xff0c;这是一个匿名函数&#xff0c;不需要写函数的名字。在不会多地方调用某个简单函数…

kubeadm来搭建k8s集群。

我们采用了二进制包搭建出的k8s集群&#xff0c;本次我们采用更为简单的kubeadm的方式来搭建k8s集群。 二进制的搭建更适合50台主机以上的大集群&#xff0c;kubeadm更适合中小型企业的集群搭建 主机配置建议&#xff1a;2c 4G 主机节点 IP …

ElementUI的Table组件行合并上手指南

ElementUI的Table组件行合并 &#xff0c;示例用官网vue3版的文档 <el-table :data"tableData" :span-method"objectSpanMethod" border style"width: 100%; margin-top: 20px"><el-table-column prop"id" label"ID&qu…

全面解析 I2C 通信协议

全面解析 I2C 通信协议 lvy 嵌入式学习规划 2023-12-22 21:20 发表于陕西 嵌入式学习规划 嵌入式软件、C语言、ARM、Linux、内核、驱动、操作系统 80篇原创内容 公众号 点击左上方蓝色“嵌入式学习规划”&#xff0c;选择“设为星标” 1、什么是I2C协议 I2C 协议是一个允许…

postman使用-03发送请求

文章目录 请求1.新建请求2.选择请求方式3.填写请求URL4.填写请求参数get请求参数在params中填写&#xff08;填完后在url中会自动显示&#xff09;post请求参数在body中填写&#xff0c;根据接口文档请求头里面的content-type选择body中的数据类型post请求参数为json-选择raw-选…

高压放大器的使用方法是什么

高压放大器是一种重要的电子设备&#xff0c;其主要功能是放大输入信号的电压&#xff0c;并输出更高电压的信号。它在各种工业、实验室和研究领域都有着广泛的应用。下面安泰电子官网将详细介绍高压放大器的使用方法以及相关注意事项。 高压放大器是一种专门用于将低电压信号转…

Unity is running with Administrator privileges, which is not supported

Unity is running with Administrator privileges, which is not supported 如果还是弹出CMD窗口提示输入密码&#xff0c;但无法怎样都无法输入&#xff0c;请关闭窗口&#xff0c;然后右键快捷方式管理员运行一次。 ----------分割线---------- 为什么这样做&#xff1f; 很…

模型量化 | Pytorch的模型量化基础

官方网站&#xff1a;Quantization — PyTorch 2.1 documentation Practical Quantization in PyTorch | PyTorch 量化简介 量化是指执行计算和存储的技术 位宽低于浮点精度的张量。量化模型 在张量上执行部分或全部操作&#xff0c;精度降低&#xff0c;而不是 全精度&#xf…

多线程编程(二)信号量

上边的函数是获取资源&#xff0c;下边的函数是释放资源。信号量就是当有多个线程争夺共享资源的时候信号量相当于管控的&#xff0c;57个人去50个位置的餐厅吃饭&#xff0c;信号量是管理开关门的呢个。 QSemaphore freesapce(buffersize);//缓冲区大小。 QSemaphore usedsp…