当前位置: 首页 > news >正文

【算法与数据结构】222、LeetCode完全二叉树的节点个数

文章目录

  • 一、题目
  • 二、一般遍历解法
  • 三、利用完全二叉树性质
  • 四、完整代码

所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。

一、题目

在这里插入图片描述

二、一般遍历解法

  思路分析:利用层序遍历,然后用num++记录节点数量。其他的例如递归法和迭代法也是如此。
  层序遍历程序如下

class Solution {
public:int countNodes(TreeNode* root) {if (!root) return 0;queue<TreeNode*> que;que.push(root);int num = 0;        // 节点数量while (!que.empty()) {int size = que.size();  // size必须固定, que.size()是不断变化的for (int i = 0; i < size; ++i) {               TreeNode* node = que.front();que.pop();num++;if (node->left) que.push(node->left);   // 空节点不入队if (node->right) que.push(node->right);}}return num;}
};

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
      递归程序如下(这应该是最精简的版本了):
class Solution2 {
public:int countNodes(TreeNode* root) {return root == NULL ? 0 : countNodes(root->left) + countNodes(root->right) + 1;}
};

三、利用完全二叉树性质

  思路分析:完全二叉树具有一个特性,假设它的深度为K,它的节点个数在 [ 2 K − 1 − 1 , 2 K − 1 ] [2^{K-1}-1, 2^K-1] [2K11,2K1]之间,意味着它只有两种情况,一种是满二叉树,一种是最后一层叶子节点没有满。对于情况一可以用 2 K − 1 2^K-1 2K1来计算,对于情况二分别递归其左子树和右子树,递归到一定深度一定有左子树或者右子树为满二叉树,然后按照情况一来计算。那么满二叉树的最左边节点和最右边节点的深度一定是相等的,依据这个特性判断子树是否为满二叉树。
在这里插入图片描述
递归程序当中,我们要确定三个步骤,1、输入参数,返回值 2、递归终止条件 3、单层递归逻辑输入参数为中间节点,返回值为左子树的节点数量+右子树节点数量+1(+1是加上中间节点)。当节点为空时,递归终止,返回0。每次递归我们都要计算最左/右边节点深度,然后判断二者是否相等,如果相等则是满二叉树,返回 2 K − 1 2^K-1 2K1,K为深度。程序当中使用了左移运算符,因为运算符的优先级问题,记得加括号。左移运算符是二进制运算,计算机计算的更快。
  程序如下

class Solution3 {
public:// 利用完全二叉树的性质,递归法int countNodes(TreeNode* root) {if (!root) return 0;TreeNode* left = root->left;TreeNode* right = root->right;int Ldepth = 0, Rdepth = 0;while (left) {      // 递归左子树left = left->left;Ldepth++;}while (right) {     // 递归右子树right = right->right;Rdepth++;}if (Ldepth == Rdepth) {return (2 << Ldepth) - 1;    // <<为左移运算符(位运算符),相当于2*leftDepth,但二进制运算计算机算的更快}return countNodes(root->left) + countNodes(root->right) + 1;}
};

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

四、完整代码

# include <iostream>
# include <vector>
# include <queue>
# include <string>
# include <algorithm>
using namespace std;// 树节点定义
struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode() : val(0), left(nullptr), right(nullptr) {}TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
};class Solution {
public:// 层序遍历法int countNodes(TreeNode* root) {if (!root) return 0;queue<TreeNode*> que;que.push(root);int num = 0;        // 节点数量while (!que.empty()) {int size = que.size();  // size必须固定, que.size()是不断变化的for (int i = 0; i < size; ++i) {               TreeNode* node = que.front();que.pop();num++;if (node->left) que.push(node->left);   // 空节点不入队if (node->right) que.push(node->right);}}return num;}
};class Solution2 {
public:int countNodes(TreeNode* root) {return root == NULL ? 0 : countNodes(root->left) + countNodes(root->right) + 1;}
};class Solution3 {
public:// 利用完全二叉树的性质,递归法int countNodes(TreeNode* root) {if (!root) return 0;TreeNode* left = root->left;TreeNode* right = root->right;int Ldepth = 0, Rdepth = 0;while (left) {      // 递归左子树left = left->left;Ldepth++;}while (right) {     // 递归右子树right = right->right;Rdepth++;}if (Ldepth == Rdepth) {return (2 << Ldepth) - 1;    // <<为左移运算符(位运算符),相当于2*leftDepth,但二进制运算计算机算的更快}return countNodes(root->left) + countNodes(root->right) + 1;}
};void my_print(vector <string>& v, string msg)
{cout << msg << endl;for (vector<string>::iterator it = v.begin(); it != v.end(); it++) {cout << *it << "  ";}cout << endl;
}void my_print2(vector<vector<int>>& v, string str) {cout << str << endl;for (vector<vector<int>>::iterator vit = v.begin(); vit < v.end(); ++vit) {for (vector<int>::iterator it = (*vit).begin(); it < (*vit).end(); ++it) {cout << *it << ' ';}cout << endl;}
}// 前序遍历迭代法创建二叉树,每次迭代将容器首元素弹出(弹出代码还可以再优化)
void Tree_Generator(vector<string>& t, TreeNode*& node) {if (t[0] == "NULL" || !t.size()) return;    // 退出条件else {node = new TreeNode(stoi(t[0].c_str()));    // 中t.assign(t.begin() + 1, t.end());Tree_Generator(t, node->left);              // 左t.assign(t.begin() + 1, t.end());Tree_Generator(t, node->right);             // 右}
}// 层序遍历
vector<vector<int>> levelOrder(TreeNode* root) {queue<TreeNode*> que;if (root != NULL) que.push(root);vector<vector<int>> result;while (!que.empty()) {int size = que.size();  // size必须固定, que.size()是不断变化的vector<int> vec;for (int i = 0; i < size; ++i) {TreeNode* node = que.front();que.pop();vec.push_back(node->val);if (node->left) que.push(node->left);   // 空节点不入队if (node->right) que.push(node->right);}result.push_back(vec);}return result;
}int main()
{vector<string> t = { "1", "2", "4", "NULL", "NULL", "5", "NULL", "NULL", "3", "6", "NULL", "NULL", "NULL"};   // 前序遍历my_print(t, "目标树");TreeNode* root = new TreeNode();Tree_Generator(t, root);vector<vector<int>> tree = levelOrder(root);my_print2(tree, "目标树:");Solution2 s1;int result = s1.countNodes(root);cout << "节点数量为:" << result << endl;system("pause");return 0;
}

end

http://www.lryc.cn/news/95264.html

相关文章:

  • 登录和注册表单的11个HTML最佳实践
  • Mysql删除历史数据
  • Python—数据结构(一)
  • 离线环境安装flask依赖包
  • ChatGPT与Claude对比分析
  • 登录和注册页面 - 验证码功能的实现
  • HDFS的文件块大小(重点)
  • 深度学习(二)
  • 无涯教程-jQuery - wrapInner( html )方法函数
  • 【unity之IMGUI实践】单例模式管理数据存储【二】
  • 【C++】开源:Linux端ALSA音频处理库
  • 【Linux | Shell】结构化命令2 - test命令、方括号测试条件、case命令
  • 基于单片机的语音识别智能垃圾桶垃圾分类的设计与实现
  • 最新版本docker 设置国内镜像源 加速办法
  • 深度学习——LSTM解决分类问题
  • three.js入门二:相机的zoom参数
  • sql语法树(select)实例
  • 爬虫002_python程序的终端运行_文件运行_ipython的使用---python工作笔记020
  • 智融SW3518S降压协议IC一款适合车充控制芯片
  • 虹科活动 | 虹科ADAS自动驾驶研讨会
  • LeetCode-每日一题-将数组和减半的最少操作次数
  • 97、Kafka的性能好在什么地方
  • (2)前端控制器的扩展配置, 视图解析器类型以及MVC执行流程的概述
  • GO学习之切片操作
  • YOLOv8实战口罩佩戴检测(视频教程)
  • SiddonGpu编译过程记录
  • Ubuntu 20.04使用 VNC远程桌面连接避坑指南
  • STM32MP157驱动开发——按键驱动(定时器)
  • 基于Centos 7虚拟机的磁盘操作(添加磁盘、分区、格式分区、挂载)
  • “完全指南:理解API和商品详情的作用和关系“