代码随想录笔记|C++数据结构与算法学习笔记-二叉树(四)|LeetCode222.完全二叉树的结点个数、110.平衡二叉树、257.二叉树的所有路径

本文主要是介绍代码随想录笔记|C++数据结构与算法学习笔记-二叉树(四)|LeetCode222.完全二叉树的结点个数、110.平衡二叉树、257.二叉树的所有路径,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 完全二叉树的结点个数
    • 思路
      • 普通二叉树的解法
      • 完全二叉树的思路
    • 伪代码实现
    • CPP代码实现
  • 平衡二叉树
    • 思路
      • 遍历顺序
    • 伪代码实现
    • CPP代码实现
  • 二叉树的所有路径
    • 思路和遍历顺序
    • 为什么会有回溯
    • 伪代码实现
    • CPP代码实现

完全二叉树的结点个数

力扣题目链接

文章链接:完全二叉树的结点个数

思路

普通二叉树的解法

确定遍历方法:后序遍历

  • 确定递归的返回值和参数
int getNum(node){}
  • 确定终止条件
if (node == NULL) return 0;
  • 单层递归的逻辑
leftnum = getNum(node->left);
rightnum = getNum(node->right);
result = leftNum + rightnum + 1;
return result;

精简代码后:

return getNum(left) + getNum(right) + 1;

完全二叉树的思路

本题既然叫做完全二叉树的结点个数,那么他肯定是利用二叉树的特性来总结出结点数量。

完全二叉树的定义:除了底层结点,上面的结点都是满的,底层结点是从左到右依次排开的

我们可以把完全二叉树和满二叉树结合起啦进行解题。如果是满二叉树,深度为 n n n,其结点个数就是 2 n − 1 2^n- 1 2n1

所以如果我们可以知道根结点的子树如果是个满二叉树,我们就直接用公式来计算。

那么就引出了以下关键问题:

  • 如何判断其子树是满二叉树。
  • 并且同时求它的深度呢?

如果它是满二叉树,那就说明向左遍历的深度和向右遍历的深度一定是相等的。并且,最后的叶子结点左右遍历后中间肯定存在着叶子结点,而不会是空的,这是完全二叉树为我们保证的。如果左右遍历的深度不相等,那肯定就不是满二叉树了,关于这一点可以画图确认。

这里的思路非常严谨,建议直接去看视频:要理解普通二叉树和完全二叉树的区别! | LeetCode:222.完全二叉树节点的数量

这个方法避免了我们取遍历一些中间的、没有必要去遍历的结点,因为完全二叉树为我们保证的。

伪代码实现

  • 确定返回值和参数
int getNum(node){}
  • 确定终止条件:遇到结点为空的情况;遇到子树为满二叉树的情况,我们要把子树的结点数量返回上去,所以我们遇到的子树是不是满二叉树,这个逻辑是写在终止条件里的
if(node == NULL) return 0;
left = node->left;//指向结点的左侧,左侧遍历计算左侧深度
right = node->right;//指向结点的右侧,右侧遍历计算右侧深度
leftdepth = 0; rightdepth = 0;
//左侧遍历统计左侧深度
while(left){left = left->left;leftdepth++;
}
//右侧遍历统计右侧深度
while(right){right = right->right;rightdepth++;
}
if (leftdepth == rightdepth)//左侧深度和右侧深度相等,说明是满二叉树return (2<<leftdepth) - 1;
  • 单层递归的逻辑
leftnum = getNum(node->left);//左
rightnum = getNum(node->right);//右
result = leftnum + rightnum + 1;//中
return result;

CPP代码实现

// 普通二叉树
class Solution {
private:int getNodesNum(TreeNode* cur) {if (cur == NULL) return 0;int leftNum = getNodesNum(cur->left);      // 左int rightNum = getNodesNum(cur->right);    // 右int treeNum = leftNum + rightNum + 1;      // 中return treeNum;}
public:int countNodes(TreeNode* root) {return getNodesNum(root);//精简//int countNodes(TreeNode* root) {//    if (root == NULL) return 0;//   return 1 + countNodes(root->left) + countNodes(root->right);//}}
};
//完全二叉树
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;}
};

平衡二叉树

力扣题目链接

后序遍历求高度,高度判断是否平衡 | LeetCode:110.平衡二叉树

平衡二叉树:左右子树的高度差小于等于1

思路

总体的思路就是判断每一个结点的左右子树的高度,看它的高度差是否超过1.

遍历顺序

求高度的话一定要用后序遍历;求深度要用前序遍历。

伪代码实现

  • 参数和返回值
int getHeight(node)
  • 确定终止条件:如果一个结点的左右子树的高度差超过了1,说明已经不是平衡二叉树了,那么当我们发现任何一个结点的左右孩子的高度不符合我们平衡二叉树的条件,我们就不返回这个结点的高度了,而是直接返回-1。
if (node == NULL) return 0;
  • 单层递归逻辑
//左右子树不符合高度差小于1的情况
//左
leftHeight = getHeight(node->left);
if (leftHeight == -1) return -1;
//右
rightHeight = getHeight(node->right);
if(rightHeight == -1) return -1;
int result;
//中
if(abs(rightHeight - leftHeight) > 1) result = -1;
else result = 1 + max(rightHeight, leftHeight);//左右子树的最大高度+父结点本身的高度等于当前结点的最大高度
return result;

CPP代码实现

class Solution {
public:// 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回-1int getHeight(TreeNode* node) {if (node == NULL) {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;}
};

二叉树的所有路径

思路和遍历顺序

求路径的过程中我们一般要使用前序遍历,因为只有前序遍历我们才能让父结点去指向它的孩子结点,这样才能吧我们的顺序按照题目输出出来。这里的遍历顺序肯定前序遍历。

为什么会有回溯

其实回溯的过程往往就在递归的下面。

本题我们是要收集路径,加入有一个容器从根结点出发收集路径1,那么等到收集路径2的时候我们还要弹出路径1,只剩下一个根结点在里面,这就是回溯的过程。

伪代码实现

  • 参数和返回值
void traversal(TreeNode* node, vector<int>& path, vector<string>& result){}
  • 确定终止条件
if (node->left == NULL && node->right == NULL){result.push_back(path);
}
  • 确定单层递归逻辑
//本题中,遍历中间结点的逻辑要写到前面来,因为我们的终止条件是到根结点的叶子结点就结束了,那就得return
//中
path.push_back(node->val);
if (node->left == NULL && node->right == NULL){result.push_back(path); return;
}
//左
if (node->left){traversal(node->left, path, result);path.pop_back(); 
}
//右
if (node->right){traversal(node->right, path, result);path.pop_back();
}

CPP代码实现

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;}
};//精简版本
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;}
};

这篇关于代码随想录笔记|C++数据结构与算法学习笔记-二叉树(四)|LeetCode222.完全二叉树的结点个数、110.平衡二叉树、257.二叉树的所有路径的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/865820

相关文章

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

使用Python实现矢量路径的压缩、解压与可视化

《使用Python实现矢量路径的压缩、解压与可视化》在图形设计和Web开发中,矢量路径数据的高效存储与传输至关重要,本文将通过一个Python示例,展示如何将复杂的矢量路径命令序列压缩为JSON格式,... 目录引言核心功能概述1. 路径命令解析2. 路径数据压缩3. 路径数据解压4. 可视化代码实现详解1

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指