代码随想录笔记|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

相关文章

Python打印对象所有属性和值的方法小结

《Python打印对象所有属性和值的方法小结》在Python开发过程中,调试代码时经常需要查看对象的当前状态,也就是对象的所有属性和对应的值,然而,Python并没有像PHP的print_r那样直接提... 目录python中打印对象所有属性和值的方法实现步骤1. 使用vars()和pprint()2. 使

Windows下C++使用SQLitede的操作过程

《Windows下C++使用SQLitede的操作过程》本文介绍了Windows下C++使用SQLite的安装配置、CppSQLite库封装优势、核心功能(如数据库连接、事务管理)、跨平台支持及性能优... 目录Windows下C++使用SQLite1、安装2、代码示例CppSQLite:C++轻松操作SQ

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

Python实例题之pygame开发打飞机游戏实例代码

《Python实例题之pygame开发打飞机游戏实例代码》对于python的学习者,能够写出一个飞机大战的程序代码,是不是感觉到非常的开心,:本文主要介绍Python实例题之pygame开发打飞机... 目录题目pygame-aircraft-game使用 Pygame 开发的打飞机游戏脚本代码解释初始化部

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

Java中Map.Entry()含义及方法使用代码

《Java中Map.Entry()含义及方法使用代码》:本文主要介绍Java中Map.Entry()含义及方法使用的相关资料,Map.Entry是Java中Map的静态内部接口,用于表示键值对,其... 目录前言 Map.Entry作用核心方法常见使用场景1. 遍历 Map 的所有键值对2. 直接修改 Ma