Files
leetcode-master/problems/0222.完全二叉树的节点个数.md
2021-11-08 14:07:24 +00:00

532 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 222.完全二叉树的节点个数
[力扣题目链接](https://leetcode-cn.com/problems/count-complete-tree-nodes/)
给出一个完全二叉树,求出该树的节点个数。
示例 1
* 输入root = [1,2,3,4,5,6]
* 输出6
示例 2
* 输入root = []
* 输出0
示例 3
* 输入root = [1]
* 输出1
提示:
* 树中节点的数目范围是[0, 5 * 10^4]
* 0 <= Node.val <= 5 * 10^4
* 题目数据保证输入的树是 完全二叉树
# 思路
本篇给出按照普通二叉树的求法以及利用完全二叉树性质的求法。
## 普通二叉树
首先按照普通二叉树的逻辑来求。
这道题目的递归法和求二叉树的深度写法类似, 而迭代法,[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)遍历模板稍稍修改一下,记录遍历的节点数量就可以了。
递归遍历的顺序依然是后序(左右中)。
### 递归
如果对求二叉树深度还不熟悉的话,看这篇:[二叉树:看看这些树的最大深度](https://programmercarl.com/0104.二叉树的最大深度.html)。
1. 确定递归函数的参数和返回值参数就是传入树的根节点返回就返回以该节点为根节点二叉树的节点数量所以返回值为int类型。
代码如下:
```
int getNodesNum(TreeNode* cur) {
```
2. 确定终止条件如果为空节点的话就返回0表示节点数为0。
代码如下:
```
if (cur == NULL) return 0;
```
3. 确定单层递归的逻辑:先求它的左子树的节点数量,再求的右子树的节点数量,最后取总和再加一 加1是因为算上当前中间节点就是目前节点为根节点的节点数量。
代码如下:
```
int leftNum = getNodesNum(cur->left); // 左
int rightNum = getNodesNum(cur->right); // 右
int treeNum = leftNum + rightNum + 1; // 中
return treeNum;
```
所以整体C++代码如下:
```CPP
// 版本一
class Solution {
private:
int getNodesNum(TreeNode* cur) {
if (cur == 0) 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);
}
};
```
代码精简之后C++代码如下:
```CPP
// 版本二
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == NULL) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
};
```
* 时间复杂度O(n)
* 空间复杂度O(logn),算上了递归系统栈占用的空间
**网上基本都是这个精简的代码版本,其实不建议大家照着这个来写,代码确实精简,但隐藏了一些内容,连遍历的顺序都看不出来,所以初学者建议学习版本一的代码,稳稳的打基础**
### 迭代法
如果对求二叉树层序遍历还不熟悉的话,看这篇:[二叉树:层序遍历登场!](https://programmercarl.com/0102.二叉树的层序遍历.html)。
那么只要模板少做改动加一个变量result统计节点数量就可以了
```CPP
class Solution {
public:
int countNodes(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
int result = 0;
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
result++; // 记录节点数量
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
};
```
* 时间复杂度O(n)
* 空间复杂度O(n)
## 完全二叉树
以上方法都是按照普通二叉树来做的,对于完全二叉树特性不了解的同学可以看这篇 [关于二叉树,你该了解这些!](https://programmercarl.com/二叉树理论基础.html),这篇详细介绍了各种二叉树的特性。
完全二叉树只有两种情况,情况一:就是满二叉树,情况二:最后一层叶子节点没有满。
对于情况一,可以直接用 2^树深度 - 1 来计算注意这里根节点深度为1。
对于情况二分别递归左孩子和右孩子递归到某一深度一定会有左孩子或者右孩子为满二叉树然后依然可以按照情况1来计算。
完全二叉树(一)如图:
![222.完全二叉树的节点个数](https://img-blog.csdnimg.cn/20201124092543662.png)
完全二叉树(二)如图:
![222.完全二叉树的节点个数1](https://img-blog.csdnimg.cn/20201124092634138.png)
可以看出如果整个树不是满二叉树,就递归其左右孩子,直到遇到满二叉树为止,用公式计算这个子树(满二叉树)的节点数量。
C++代码如下:
```CPP
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
TreeNode* left = root->left;
TreeNode* right = root->right;
int leftHeight = 0, rightHeight = 0; // 这里初始为0是有目的的为了下面求指数方便
while (left) { // 求左子树深度
left = left->left;
leftHeight++;
}
while (right) { // 求右子树深度
right = right->right;
rightHeight++;
}
if (leftHeight == rightHeight) {
return (2 << leftHeight) - 1; // 注意(2<<1) 相当于2^2所以leftHeight初始为0
}
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
```
* 时间复杂度O(logn * logn)
* 空间复杂度O(logn)
# 其他语言版本
## Java
```java
class Solution {
// 通用递归解法
public int countNodes(TreeNode root) {
if(root == null) {
return 0;
}
return countNodes(root.left) + countNodes(root.right) + 1;
}
}
```
```java
class Solution {
// 迭代法
public int countNodes(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int result = 0;
while (!queue.isEmpty()) {
int size = queue.size();
while (size -- > 0) {
TreeNode cur = queue.poll();
result++;
if (cur.left != null) queue.offer(cur.left);
if (cur.right != null) queue.offer(cur.right);
}
}
return result;
}
}
```
```java
class Solution {
/**
* 针对完全二叉树的解法
*
* 满二叉树的结点数为2^depth - 1
*/
public int countNodes(TreeNode root) {
if(root == null) {
return 0;
}
int leftDepth = getDepth(root.left);
int rightDepth = getDepth(root.right);
if (leftDepth == rightDepth) {// 左子树是满二叉树
// 2^leftDepth其实是 2^leftDepth - 1 + 1 ,左子树 + 根结点
return (1 << leftDepth) + countNodes(root.right);
} else {// 右子树是满二叉树
return (1 << rightDepth) + countNodes(root.left);
}
}
private int getDepth(TreeNode root) {
int depth = 0;
while (root != null) {
root = root.left;
depth++;
}
return depth;
}
}
```
## Python
递归法:
```python
class Solution:
def countNodes(self, root: TreeNode) -> int:
return self.getNodesNum(root)
def getNodesNum(self, cur):
if not cur:
return 0
leftNum = self.getNodesNum(cur.left) #左
rightNum = self.getNodesNum(cur.right) #右
treeNum = leftNum + rightNum + 1 #中
return treeNum
```
递归法:精简版
```python
class Solution:
def countNodes(self, root: TreeNode) -> int:
if not root:
return 0
return 1 + self.countNodes(root.left) + self.countNodes(root.right)
```
迭代法:
```python
import collections
class Solution:
def countNodes(self, root: TreeNode) -> int:
queue = collections.deque()
if root:
queue.append(root)
result = 0
while queue:
size = len(queue)
for i in range(size):
node = queue.popleft()
result += 1 #记录节点数量
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return result
```
完全二叉树
```python
class Solution:
def countNodes(self, root: TreeNode) -> int:
if not root:
return 0
left = root.left
right = root.right
leftHeight = 0 #这里初始为0是有目的的为了下面求指数方便
rightHeight = 0
while left: #求左子树深度
left = left.left
leftHeight += 1
while right: #求右子树深度
right = right.right
rightHeight += 1
if leftHeight == rightHeight:
return (2 << leftHeight) - 1 #注意(2<<1) 相当于2^2所以leftHeight初始为0
return self.countNodes(root.left) + self.countNodes(root.right) + 1
```
## Go
递归版本
```go
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
//本题直接就是求有多少个节点,无脑存进数组算长度就行了。
func countNodes(root *TreeNode) int {
if root == nil {
return 0
}
res := 1
if root.Right != nil {
res += countNodes(root.Right)
}
if root.Left != nil {
res += countNodes(root.Left)
}
return res
}
```
利用完全二叉树特性的递归解法
```go
func countNodes(root *TreeNode) int {
if root == nil {
return 0
}
leftH, rightH := 0, 0
leftNode := root.Left
rightNode := root.Right
for leftNode != nil {
leftNode = leftNode.Left
leftH++
}
for rightNode != nil {
rightNode = rightNode.Right
rightH++
}
if leftH == rightH {
return (2 << leftH) - 1
}
return countNodes(root.Left) + countNodes(root.Right) + 1
}
```
## JavaScript:
递归版本
```javascript
var countNodes = function(root) {
//递归法计算二叉树节点数
// 1. 确定递归函数参数
const getNodeSum=function(node){
//2. 确定终止条件
if(node===null){
return 0;
}
//3. 确定单层递归逻辑
let leftNum=getNodeSum(node.left);
let rightNum=getNodeSum(node.right);
return leftNum+rightNum+1;
}
return getNodeSum(root);
};
```
迭代(层序遍历)版本
```javascript
var countNodes = function(root) {
//层序遍历
let queue=[];
if(root===null){
return 0;
}
queue.push(root);
let nodeNums=0;
while(queue.length){
let length=queue.length;
while(length--){
let node=queue.shift();
nodeNums++;
node.left&&queue.push(node.left);
node.right&&queue.push(node.right);
}
}
return nodeNums;
};
```
利用完全二叉树性质
```javascript
var countNodes = function(root) {
//利用完全二叉树的特点
if(root===null){
return 0;
}
let left=root.left;
let right=root.right;
let leftHeight=0,rightHeight=0;
while(left){
left=left.left;
leftHeight++;
}
while(right){
right=right.right;
rightHeight++;
}
if(leftHeight==rightHeight){
return Math.pow(2,leftHeight+1)-1;
}
return countNodes(root.left)+countNodes(root.right)+1;
};
```
## C:
递归法
```c
int countNodes(struct TreeNode* root) {
//若传入结点不存在返回0
if(!root)
return 0;
//算出左右子树的结点总数
int leftCount = countNodes(root->left);
int rightCount = countNodes(root->right);
//返回左右子树结点总数+1
return leftCount + rightCount + 1;
}
int countNodes(struct TreeNode* root){
return getNodes(root);
}
```
迭代法
```c
int countNodes(struct TreeNode* root){
//记录结点总数
int totalNum = 0;
//开辟栈空间
struct TreeNode** stack = (struct TreeNode**)malloc(sizeof(struct TreeNode*) * 100);
int stackTop = 0;
//如果root结点不为NULL则将其入栈。若为NULL则不会进入遍历返回0
if(root)
stack[stackTop++] = root;
//若栈中有结点存在,则进行遍历
while(stackTop) {
//取出栈顶元素
struct TreeNode* tempNode = stack[--stackTop];
//结点总数+1
totalNum++;
//若栈顶结点有左右孩子,将它们入栈
if(tempNode->left)
stack[stackTop++] = tempNode->left;
if(tempNode->right)
stack[stackTop++] = tempNode->right;
}
return totalNum;
}
```
满二叉树
```c
int countNodes(struct TreeNode* root){
if(!root)
return 0;
int leftHeight = 0;
int rightHeight = 0;
struct TreeNode* rightNode = root->right;
struct TreeNode* leftNode = root->left;
//求出左子树深度
while(leftNode) {
leftNode = leftNode->left;
leftHeight++;
}
//求出右子树深度
while(rightNode) {
rightNode = rightNode->right;
rightHeight++;
}
//若左右子树深度相同为满二叉树。结点个数为2^height-1
if(rightHeight == leftHeight) {
return (2 << leftHeight) - 1;
}
//否则返回左右子树的结点个数+1
return countNodes(root->right) + countNodes(root->left) + 1;
}
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>