LeetCode222. 完全二叉树的节点个数(二分查找+二进制表示路径法)

news/2024/10/29 7:25:09/

一、题目描述

给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

在这里插入图片描述
注:
树中节点的数目范围是[0, 5 * 104]
0 <= Node.val <= 5 * 104
题目数据保证输入的树是 完全二叉树

二、题目解析&思路分析

像我拿到这道题,那这还不简单,遍历计数不就搞定了嘛?
前几天刚好复习了二叉树的遍历方法,直接拿出来我最拿手的,也是最基础的 BFS(广度优先遍历,Breath First Search)

基础的二叉树遍历还不太了解的同学可以先看下这篇博客:
数据结构之二叉树构建、广度/深度优先(前序、中序、后序)遍历

话不多说直接上代码分析:

2.1 BFS 广度优先遍历计数法

class Solution {
public:int countNodes(TreeNode* root) {int count = 0;//计数器if(root == nullptr){return count;//空树节点为 0 }else{count = 1;//根节点}queue<TreeNode*> quTemp;//利用队列来控制遍历顺序quTemp.push(root);while(!quTemp.empty()){//依次访问每个节点的左孩子右孩子并计数TreeNode* nodeTemp = quTemp.front();quTemp.pop();if(nodeTemp->left != nullptr){count++;quTemp.push(nodeTemp->left);}if(nodeTemp->right != nullptr){count++;quTemp.push(nodeTemp->right);}}return count;}
};

没啥问题,好的提交运行看一下:
在这里插入图片描述
有点鸡肋啊,那我们换一种?

2.2 深度优先遍历

深度优先咱们不在此阐述了,不了解的还是照旧看上面的那个博客
咱们还是直接看代码:

class Solution {
public:int countNodes(TreeNode* root) {int count = 0;if(root == nullptr){return count;}else{DLR(count, root);return count;}}void DLR(int& count, TreeNode* root)//根左右{if(root == nullptr){return;}count++;DLR(count, root->left);DLR(count, root->right);}
};

再看运行结果:
在这里插入图片描述
在这里插入图片描述
leetcode对时间判定还是和电脑设备,网速有一定关系的

深度和广度这两时间上都是(O(n),不过深度优先遍历没有额外用队列来进行辅助,因此空间上是低于BFS的。

难道只能止步于50%多?还有没有更优的解法呢?

2.3 重新解读题目

题目给到的是完全二叉树,那么我们可以知道
完全二叉树:
所有的节点 从 0~n ,从上到下、从左至右,节点都是连续的,先有左孩子才有右孩子节点。
那么这样假设
1.全满节点的二叉树是满二叉树 每一层的节点数都达到最大值,假设树的高度为 h 那么 节点数就是 2^h -1
2.最后一层未满,那么和下图一样最后一个节点肯定在最后一层:
在这里插入图片描述
那么我们只需要知道树的高度 和 最后一层的最后一个节点是多少即可知道整颗二叉树的节点数
注:这里题目中没有说节点的值是从上到下,从左到右 是从 1~n 按顺序排列 但是实际上就是如此。

2.4 二分查找 + 二进制表示路径法

由以上分析可以得知我们更优的解法是

需要知道树的高度 和 最后一层的最后一个节点是多少即可知道整颗二叉树的节点数

2.4.1 树的高度

树的高度很容易得出,因为是完全二叉树,那么我们只需要从根节点一直往左孩子遍历即可得知树的高度

        int level = 0;//层数 从 0 开始TreeNode* node = root;while (node->left != nullptr) {level++;node = node->left;}

2.4.2 二分查找

查找某个值是否存在
在这里插入图片描述
通过二分查找我们就可以知道最右边节点是哪一个,因此可以直接将该值返回,该值就是节点数。

代码实现:

        int left = 1 << level, right = (1 << (level + 1)) - 1;while (left < right) {int mid = (right - left + 1) / 2 + left;if (Is_Exist(root, level, mid)) {left = mid;} else {right = mid - 1;}}return left;

但是这里有一个问题,上图讲的是在一个数组中进行值判断,而我们如何判断二叉树得最后一层得节点值是否存在呢?
继续往下看

2.4.3 该数字(节点)是否存在

我们先观察每个节点的值的二进制
在这里插入图片描述
我们根据上图节点得值做出假设:
0 代表向 左
1 代表向 右

总结以下规律:
每个节点的值的部分二进制代表该节点从根节开始走向的路径 例如:
8 :000 代表 从根节点 向左 ->向左 ->向左
11 :011 代表 从根节点 向左 -> 向右 -> 向右

二进制表示路径图

知道 二进制 如何表示 节点的路径之后 我们只需要从左至右逐个把每一位取出来取出来是 0 就是 从根节点往左走取出来是 1 表示从根节点往右走,这样一直走下去,最后判断这个节点 node 是不是 nullptr 就可以判断这个节点是不是存在。

代码示例:

    bool Is_Exist(TreeNode* root, int level, int k){//例如第 3 层 有 3 个 bit 位表示 路径 那么 就用 0100 按位 & 取即可int bits = 1 << (level - 1);TreeNode* node = root;while (node != nullptr && bits > 0) {if (bits & k) {//是 1 就 往右走node = node->right;} else {//是 0 就往左走node = node->left;}//取出来一位判断之后 继续取下一位bits >>= 1;}//最后判断这个节点是不是 nullptr 即可判断该节点存不存在return node != nullptr;}

2.4.4 时间复杂度于空间复杂度分析

leetcode 复杂度分析:
在这里插入图片描述

三、代码实现

class Solution {
public:int countNodes(TreeNode* root) {if (root == nullptr) {return 0;}//二叉树层高从 0 开始int level = 0;TreeNode* node = root;while (node->left != nullptr) {level++;node = node->left;}//二分查找最后一层的每个节点是否存在int left = 1 << level, right = (1 << (level + 1)) - 1;while (left < right) {//每次判断 mid 是否存在int mid = (right - left + 1) / 2 + left;if (Is_Exist(root, level, mid)) {left = mid;} else {right = mid - 1;}}return left;}bool Is_Exist(TreeNode* root, int level, int k){//例如第 3 层 有 3 个 bit 位表示 路径 那么 就用 100 按位 & 取即可int bits = 1 << (level - 1);TreeNode* node = root;while (node != nullptr && bits > 0) {if (bits & k) {//是 1 就 往右走node = node->right;} else {//是 0 就往左走node = node->left;}//取出来一位判断之后 继续取下一位bits >>= 1;}//最后判断这个节点是不是 nullptr 即可判断该节点存不存在return node != nullptr;}
};

运行结果:可以看到时间效率大幅度提升
在这里插入图片描述


http://www.ppmy.cn/news/37922.html

相关文章

【vim进阶】vim编辑器的分屏操作(分屏显示文件,关闭分屏,分屏间光标的移动,移动分屏)

一、分屏显示文件 VIM 可以实现分屏操作&#xff0c;一个屏幕被多个文件给分占&#xff0c;有左右和上下两种分屏的方式。 方法一&#xff1a;启动分屏 左右分屏如下操作&#xff1a; vim -On file1 file2 ... filenn是数字&#xff0c;表示分屏的数量,n要大于等于文件个数…

设计模式---装饰模式

目录 介绍 实现 优缺点 装饰模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。这种类型的设计模式属于结构型模式&#xff0c;它是作为现有类的一个包装。这种模式创建了一个装饰类&#xff0c;用来包装原有…

【新2023Q2押题JAVA】华为OD机试 - 找字符

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:找字符 题目 给定两个字符串…

目标检测算法——农业作物开源数据集汇总(收藏)

&#x1f384;&#x1f384;近期&#xff0c;小海带在空闲之余收集整理了一批农业作物开源数据集资源供大家参考。 整理不易&#xff0c;小伙伴们记得一键三连喔&#xff01;&#xff01;&#xff01;&#x1f388;&#x1f388; 一、农作物图像分类&#xff08;小麦、睡到、甘…

第02章_MySQL环境搭建

第02章_MySQL环境搭建 &#x1f3e0;个人主页&#xff1a;shark-Gao &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是shark-Gao&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f389;目前状况&#xff1a;23届毕业生&#xff0c;目前…

jsp823科研项目申报管理网站cc94程序mysql+java

具体功能 1&#xff0e;系统登录&#xff1a;系统登录是用户访问系统的路口&#xff0c;设计了系统登录界面&#xff0c;包括用户名、密码和验证码&#xff0c;然后对登录进来的用户判断身份信息&#xff0c;判断是管理员用户还是普通用户。 2&#xff0e;科研项目申请&#xf…

怎么把pdf转换成高清图片

怎么把pdf转换成高清图片&#xff1f;可以使用以下两种方法&#xff1a; 方法一&#xff1a;使用Adobe Acrobat Pro DC 1、打开需要转换的PDF文件&#xff0c;点击“文件”菜单中的“导出为”&#xff0c;在弹出的菜单中选择“图像”&#xff0c;然后选择“JPEG”。 2、在“…

计算机网络 常见网卡信息

文章目录1. PCI 网卡2. PCI Express 网卡3. USB网卡4. 无线网卡万兆网卡光纤网卡1. PCI 网卡 接口类型&#xff1a;PCI 传输速率&#xff1a;10/100Mbps或1000Mbps 支持协议&#xff1a;TCP/IP、UDP、IPX/SPX等 缓存大小&#xff1a;通常为64KB或128KB 2. PCI Express 网卡 …