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

news2024/11/17 23:49:59

文章目录

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

所有的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.coloradmin.cn/o/791874.html

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

相关文章

lama cleaner

这里写自定义目录标题 安装参数包含的额外plugins 安装 conda create --name lamacleaner python3.10 pip install -r requirements.txt pip install gfpgan pip install realesrgan pip install rembg pip install .如果安装本package报错&#xff0c;可以尝试改&#xff1…

【2023】Nacos下载与安装配置(2.2.3版本示例)

1、Nacos概述 1.1 什么是Nacos Nacos 是阿里巴巴推出来的一个新开源项目&#xff0c;这是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集&#xff0c;帮助您快速实现动态服…

AndroidStudio Memory profiler(内存分析器)

1.Record Java/Kotlin allocations 查看java 层中对象的调用栈和短时间内创建对象的次数。可用于内存抖动快速分析,可用快速查找到该对象的调用栈(等同于mat) 从上图可见&#xff0c;短时间内创建了23个char[] 数组&#xff0c;其中最大的char[] 占用20k, 查看cll stack 调用…

前端将css.html.js打包到一起打开

过程我是按照下面的执行的&#xff0c;大家可以直接参考这个博客里的过程&#xff0c;下面我记录一下遇到的一些问题&#xff0c;我的电脑是mac 打包教程 1.执行命令npm install electron 在安装Electron时报错command sh -c node install.js 在指令后面添加 --ignore-scripts…

火山引擎DataLeap如何解决SLA治理难题(二):申报签署流程与复盘详解

申报签署流程详解 火山引擎DataLeap SLA保障的前提是先达成SLA协议。在SLA保障平台中&#xff0c;以 申报单签署的形式达成SLA协议。平台核心特点是 优化了SLA达成的流程&#xff0c;先通过 “系统卡点计算”减少待签署任务的数量&#xff0c;再通过 “SLA推荐计算”自动签署部…

No module named ‘_bz2‘

【问题】 mmlab使用custom_imports加载失败&#xff0c;调试到里面去发现import时引发error&#xff1a; ModuleNotFoundError: No module named ‘_bz2’ 【解决】 参考https://stackoverflow.com/questions/50335503/no-module-named-bz2-in-python3靠后的回答。 下载文件…

Vue学习Day2——指令补充

一、指令修饰符 1、什么是指令修饰符&#xff1f; ​ 所谓指令修饰符就是通过“.”指明一些指令后缀 不同的后缀封装了不同的处理操作 —> 简化代码 2、按键修饰符 keyup.enter —>当点击enter键的时候才触发 代码演示&#xff1a; <!DOCTYPE html> <html…

大模型开发(十三):Function calling调用外部工具API,实现实时天气查询

全文共1.2w余字&#xff0c;预计阅读时间约34~50分钟 | 满满干货(附代码案例)&#xff0c;建议收藏&#xff01; 本文目标&#xff1a;完整构建一个借助Function calling功能调用外部工具API的开发流程&#xff0c;实现天气信息的实时查询 本文代码切换使用gpt3.5和gpt4接口&a…

【雕爷学编程】Arduino动手做(88)---水流量传感器模块3

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

低代码开发为什么能长盛不衰?

低代码是一种通过可视化的界面与配置为开发者提供一个快速创建应用软件的开发环境的技术。2018年&#xff0c;西门子收购低代码企业Mendix、美国低代码独角兽企业Outsystems获得1.5亿美元的融资&#xff0c;这两个事件的发生将低代码市场带入资本方的视野&#xff0c;中国低代码…

背包问题(1)------动态规划

你有一个背包&#xff0c;地上一堆物品&#xff0c;挑选一些物品放入背包中&#xff0c;最大能够挑选出来的价值是多少 背包可以装满&#xff0c;背包也是可以不必都装满 一)01背包问题 【模板】01背包_牛客题霸_牛客网 (nowcoder.com) 1)求这个背包最多可以装多大价值的物品&a…

Java编程实现遍历两个MAC地址之间所有MAC的方法

Java编程实现遍历两个MAC地址之间所有MAC的方法 本文实例讲述了java编程实现遍历两个MAC地址之间所有MAC的方法。分享给大家供大http://家参考&#xff0c;具体如下&#xff1a; 在对发放的设备进行后台管理时,很多时候会用到设备MAC这个字段,它可以标识唯一一个设备。然而在数…

【Axure高保真原型】图片放大镜效果

今天和大家分享图片放大镜效果的原型模板&#xff0c;鼠标移入图片区域后&#xff0c;会显示放大的方框&#xff0c;方框会跟随图片移动&#xff0c;右侧会显示方框区域的大图&#xff0c;具体效果可以观看下方视频或者打开预览地址体验。 【原型效果】 【Axure高保真原型】图…

一文详解Spring Bean循环依赖

一、背景 有好几次线上发布老应用时&#xff0c;遭遇代码启动报错&#xff0c;具体错误如下&#xff1a; Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name xxxManageFacadeImpl: Bean with name xxxManageFa…

centos7搭建airflow2.6.3教程

一、环境说明&#xff1a; python3.9.6mysql5.7数据库 基础环境自行安装&#xff0c;本教程不包含基础环境部分 二、安装airflow2.6.3 1.安装Linux系统依赖模块 yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel…

PSP - 使用 MMseqs2 工具快速搜索蛋白质序列数据库 (GMGC)

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/131934642 MMseq2 是非常强大和高效的生物信息学软件&#xff0c;可以在极短的时间内对大规模的核苷酸和蛋白质序列进行搜索和聚类。主要特点有&a…

【2022】贝壳找房秋招C++工程师笔试卷1

题解&#xff1a;直接暴力 class Solution { public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** * param s string字符串 * param k int整型 * return string字符串*/string NS_String(string s, int k) {// writ…

【C语言day07】

在调用函数的时候&#xff0c;真实传递给函数的是实参&#xff0c;函数定义部分函数名后的参数是形参。 形参和实参的名字是可以相同的&#xff0c;在函数调用的时候&#xff0c;形参是实参的一份临时拷贝&#xff0c;分别占用不同的内存空间&#xff0c;所以A正确&#xff0c;…

智慧园区电力监控解决方案

1、概述 电力监控系统实现对园区变电站、配电房内断路器、变压器、柴油发电机以及其它重要设备进行监视、测量、记录、报警等功能&#xff0c;并与保护设备和远方控制中心及其他设备通信&#xff0c;实时掌握园区变电站和配电房运行状况&#xff0c;快速排除故障&#xff0c;保…