【面试经典 150 | 二叉树】完全二叉树的节点个数

news2024/10/6 1:39:30

文章目录

  • 写在前面
  • Tag
  • 题目来源
  • 解题思路
    • 方法一:遍历统计
    • 方法二:二分查找+位运算
  • 写在最后

写在前面

本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更……

专栏内容以分析题目为主,并附带一些对于本题涉及到的数据结构等内容进行回顾与总结,文章结构大致如下,部分内容会有增删:

  • Tag:介绍本题牵涉到的知识点、数据结构;
  • 题目来源:贴上题目的链接,方便大家查找题目并完成练习;
  • 题目解读:复述题目(确保自己真的理解题目意思),并强调一些题目重点信息;
  • 解题思路:介绍一些解题思路,每种解题思路包括思路讲解、实现代码以及复杂度分析;
  • 知识回忆:针对今天介绍的题目中的重点内容、数据结构进行回顾总结。

Tag

【完全二叉树】【二分+位运算】


题目来源

222. 完全二叉树的节点个数


解题思路

方法一:遍历统计

思路

使用前序、中序、后序以及层序遍历中的任何一种遍历方法,即可统计完全二叉树的节点个数。这个方法也适应一切二叉树。

以下是前序遍历方法的迭代实现。

代码

/**
 * Definition for a binary tree node.
 * 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 {
private:
    int res = 0;
public:
    void preOrder(TreeNode* root) {
        if (!root) {
            return;
        }
        res += 1;
        preOrder(root->left);
        preOrder(root->right);
    }

    int countNodes(TreeNode* root) {
        preOrder(root);
        return res;
    }
};

复杂度分析

时间复杂度: O ( n ) O(n) O(n) n n n 是完全二叉树的节点数。

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

方法二:二分查找+位运算

方法一没有用到 完全二叉树 这个条件,仅仅是使用一般的方法就可以解答。

现在考虑如何充分利用完全二叉树这个条件。完全二叉树除了最后一层可能不满,其余每一层都是满的。这样的话,我们可以直接计算二叉树的前 h-1 层的节点总数(设完全二叉树一共 h 层)。当 0 ≤ i < h 0 \le i < h 0i<h 时,第 i i i 层包含 2 i 2^i 2i 个节点,前 h-1 层的节点共计:

∑ i = 0 h − 1 2 i \sum_{i=0}^{h-1}{2^i} i=0h12i

最后一层的节点数怎么求呢?因为是完全二叉树,所以最后一层的节点数范围为 [ 1 , 2 h ] [1, 2^h] [1,2h]。于是对于最大层数为 h 的完全二叉树,节点个数一定在 [ 2 h , 2 h + 1 − 1 ] [2^h, 2^{h+1}-1] [2h,2h+11] 范围内。

此时可以通过二分查找确定完全二叉树的节点个数。

具体地,根据节点个数范围的上下界得到当前需要判断的节点个数 k,如果第 k 个节点存在,则节点个数一定 大于等于 k;如果第 k 个节点不存在,则节点个数一定小于 k,由此可以将查找范围缩小一半,直到得到节点个数。

位运算

如何判断第 k 个节点是否存在呢?如果第 k 个节点位于第 h 层,则 k 的二进制表示包含 h+1(0 到 h) 位,其中最高位是 1。其余各位从高到低表示从根节点到第 k 个节点的路径,0 表示移动到左子节点,1 表示移动到右子节点。通过位运算得到第 k 个节点对应的路径,判断该路径对应的节点是否存在,即可判断第 k 个节点是否存在。

示例 1 的完全二叉树以及二进制位表示以及如何查找指定节点如图所示。

代码

/**
 * Definition for a binary tree node.
 * 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:
    bool exits(TreeNode* root, int depth, int mid) {
        int bits = 1 << (depth - 1);        // 
        TreeNode* node = root;
        while(node != nullptr && bits > 0) {
            if ((bits & mid)) {
                node = node->right;
            }
            else {
                node = node->left;
            }
            bits >>= 1;
        }
        return node != nullptr;
    }

    int countNodes(TreeNode* root) {
        if (root == nullptr) {
            return 0;
        }

        // 求完全二叉树最大深度 h
        int depth = 0;
        TreeNode* node = root;
        while(node->left != nullptr) {
            ++depth;
            node = node->left;
        }

        // 确定二分法的二分范围 2^{h} 到 2^{h+1}-1
        int low = 1 << depth;
        int high = (1 << (depth + 1)) - 1;
        while (low < high) {
            int mid = (high - low + 1) / 2 + low;
            if (exits(root, depth, mid)) {
                low = mid;
            }
            else {
                high = mid - 1;
            }
        }
        return low;
    }
};

复杂度分析

时间复杂度: O ( l o g 2 n ) O(log^2n) O(log2n),其中 n n n 是完全二叉树的节点数。首先需要 O ( h ) O(h) O(h) 的时间得到完全二叉树的最大层数,其中 h h h 是完全二叉树的最大层数。
使用二分查找确定节点个数时,需要查找的次数为 O ( l o g ⁡ 2 h ) = O ( h ) O(log⁡^2h)=O(h) O(log2h)=O(h),每次查找需要遍历从根节点开始的一条长度为 h h h 的路径,需要 O ( h ) O(h) O(h) 的时间,因此二分查找的总时间复杂度是 O ( h 2 ) O(h^2) O(h2),由于本题是二叉搜索树所以有 O ( h ) = O ( l o g n ) O(h)=O(logn) O(h)=O(logn),因方法二的时间复杂度为 O ( l o g 2 n ) O(log^2n) O(log2n)

空间复杂度: O ( 1 ) O(1) O(1)


写在最后

如果您发现文章有任何错误或者对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

如果大家有更优的时间、空间复杂度的方法,欢迎评论区交流。

最后,感谢您的阅读,如果有所收获的话可以给我点一个 👍 哦。

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

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

相关文章

北斗引路,太阳为源,定位报警,保护渔业,安全护航!

2022年1月&#xff0c;农业农村部发布《“十四五”全国渔业发展规划》明确提出&#xff0c;到2025年&#xff0c;渔业质量效益和竞争力明显增强&#xff0c;渔业基础设施和装备条件明显改善&#xff0c;渔业治理体系和治理能力现代化水平明显提高&#xff0c;实现产业更强、生态…

剖析线程池:深入理解Java中的线程池构造和调优技巧

使用Executors工具类创建线程池 Executors的主要方法与默认配置 Executors 工具类是 Java 中创建线程池的标准方法之一&#xff0c;它提供了许多静态方法来创建不同类型的线程池。以下是一些常用的 Executors 方法及其作用&#xff1a; newFixedThreadPool(int nThreads): 创…

Git如何配合Github使用

1.安装Git https://git-scm.com/ ##2.配置 Git 安装完成后&#xff0c;你需要设置 Git 的用户名和邮箱地址&#xff0c;这样在提交代码时就能知道是谁提交的。你可以在命令行中输入以下命令来配置&#xff1a; git config --global user.name "Your Name" git con…

政安晨:【Keras机器学习示例演绎】(十八)—— 图像字幕

目录 设置 下载数据集 准备数据 将文本数据向量化 构建用于训练的tf.data.Dataset管道 构建模型 模型训练 检查样本预测结果 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政安晨的博客能够对…

ChuanhuChatGPT集成百川大模型

搭建步骤&#xff1a; 拷贝本地模型&#xff0c;把下载好的Baichuan2-7B-Chat拷贝到models目录下 修改modules\models\base_model.py文件&#xff0c;class ModelType增加Baichuan Baichuan 16 elif "baichuan" in model_name_lower: model_type ModelType.Ba…

8点法估计基础矩阵

估计基础矩阵 文章目录 估计基础矩阵8点法归一化 8点法 8点法 根据两幅图像中8个对应点对之间的关系&#xff0c;采用SVD求 解最小二乘方 约束&#xff1a;det(F) 0 假设已知N对点的对应关系&#xff1a; { x i , x i ′ } i 1 N \{x_i,x^{\prime}_i\}_{i1}^N {xi​,xi′​…

第一个大型汽车ITU-T车载语音通话质量实验室投入使用

中国汽车行业蓬勃发展&#xff0c;尤其是新能源汽车风起云涌&#xff0c;无论是国内还是海外需求旺盛的趋势下&#xff0c;除乘用车等紧凑型车外&#xff0c;中型汽车如MPV、小巴、小型物流车&#xff0c;大型汽车如重卡、泥头车等亦加入了手机互联、智驾的科技行列&#xff0c…

力扣题目:轮转数组

力扣题目&#xff1a;轮转数组 题目链接: 189.轮转数组 题目描述 代码思路 根据从轮转前到轮转后到数组变化&#xff0c;我们可以将数组元素分成两个部分&#xff0c;一个部分数轮转后从右边调到前面&#xff0c;一部分仅仅从左边向右移动。发现这个规律后&#xff0c;将数组…

软件工程的介绍

软件工程 这一章的内容其实还是蛮多的,大概一共有10个章节,分别是下面的一些内容,但是呢,这一章的内容其实是比较偏向文科类的,也就是说,记忆的内容其实占有很大的篇幅,在该考试科目当中呢,其实也是主要影响上午题部分的选择题的考察,基本的分值呢,在10分左右,分值占…

python自定义交叉熵损失,再和pytorch api对比

背景 我们知道&#xff0c;交叉熵本质上是两个概率分布之间差异的度量&#xff0c;公式如下 其中概率分布P是基准&#xff0c;我们知道H(P,Q)>0&#xff0c;那么H(P,Q)越小&#xff0c;说明Q约接近P。 损失函数本质上也是为了度量模型和完美模型的差异&#xff0c;因此可以…

input框添加验证(如只允许输入数字)中文输入导致显示问题的解决方案

文章目录 input框添加验证(如只允许输入数字)中文输入导致显示问题的解决方案问题描述解决办法 onCompositionStart与onCompositionEnd input框添加验证(如只允许输入数字)中文输入导致显示问题的解决方案 问题描述 测试环境&#xff1a;react antd input (react的事件与原生…

如何在TestNG中忽略测试用例

在这篇文章中&#xff0c;我们将讨论如何在TestNG中忽略测试用例。TestNG帮助我们忽略使用Test注释的情况&#xff0c;我们可以在不同的级别上忽略这些情况。 首先&#xff0c;只忽略一个测试方法或测试用例。第二&#xff0c;忽略一个类及其子类中的所有情况。第三个是&#…

QT中基于TCP的网络通信

QT中基于TCP的网络通信 QTcpServer公共成员函数信号 QTcpSocket公共成员函数信号 通信流程服务器端通信流程代码 客户端通信流程代码 使用Qt提供的类进行基于TCP的套接字通信需要用到两个类&#xff1a; QTcpServer&#xff1a;服务器类&#xff0c;用于监听客户端连接以及和客…

【算法基础实验】图论-UnionFind连通性检测之quick-find

Union-Find连通性检测之quick-find 理论基础 在图论和计算机科学中&#xff0c;Union-Find 或并查集是一种用于处理一组元素分成的多个不相交集合&#xff08;即连通分量&#xff09;的情况&#xff0c;并能快速回答这组元素中任意两个元素是否在同一集合中的问题。Union-Fin…

RDD编程初级实践

参考链接 spark入门实战系列--8MLlib spark 实战_mob6454cc68310b的技术博客_51CTO博客https://blog.51cto.com/u_16099212/7454034 Spark和Hadoop的安装-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/138021948?spm1001.2014.3001.5501 1. spark-shell…

Linux的学习之路:22、线程(2)

摘要 本章继续讲一下线程的东西 目录 摘要 一、抢票 二、加锁保护 三、死锁 1、死锁四个必要条件 2、避免死锁 四、同步 1、常见的线程安全的情况 2、常见不可重入的情况 3、常见可重入的情况 4、可重入与线程安全联系 5、可重入与线程安全区别 一、抢票 这里回…

大模型咨询培训老师叶梓:利用知识图谱和Llama-Index增强大模型应用

大模型&#xff08;LLMs&#xff09;在自然语言处理领域取得了显著成就&#xff0c;但它们有时会产生不准确或不一致的信息&#xff0c;这种现象被称为“幻觉”。为了提高LLMs的准确性和可靠性&#xff0c;可以借助外部知识源&#xff0c;如知识图谱。那么我们如何通过Llama-In…

clickhouse与oracle传输数据

参考 https://github.com/ClickHouse/clickhouse-jdbc-bridge https://github.com/ClickHouse/clickhouse-jdbc-bridge/blob/master/docker/README.md clickhouse官方提供了一种方式&#xff0c;可以实现clickhouse与oracle之间传输数据&#xff0c;不仅仅是oracle&#xff0…

Java后端利用百度地图全球逆地理编码,获取地址

声明&#xff1a;本人是在实习项目的时候遇到的问题 一.使用Api分为四步骤全球逆地理编码 rgc 反geo检索 | 百度地图API SDK 步骤1,2自行完成 接下来去获取AK 二.申请AK 登录百度账号 点击创建应用&#xff0c;选择自己想用的服务&#xff0c;我只单选了逆地理编码&#xff…

debian gnome-desktop GUI(图形用户界面)系统

目录 &#x1f31e;更新 &#x1f3a8;安装 &#x1f34e;分配 &#x1f6cb;️重启 &#x1f511;通过VNC连接 debian gnome-desktop &#x1f31e;更新 sudo apt update sudo apt -y upgrade &#x1f3a8;安装 sudo apt -y install task-gnome-desktop 这个过程比…