题目主要信息:
- 判断给定二叉树是否为完全二叉树
- 首先我们需要知道什么是完全二叉树:叶子节点只能出现在最下层和次下层,且最下层的叶子节点集中在树的左部。
- 需要注意的是,满二叉树肯定是完全二叉树,而完全二叉树不一定是满二叉树。
举一反三:
学习完本题的思路你可以解决如下题目:
方法:层次遍历(推荐使用)
知识点:队列
队列是一种仅支持在表尾进行插入操作、在表头进行删除操作的线性表,插入端称为队尾,删除端称为队首,因整体类似排队的队伍而得名。它满足先进先出的性质,元素入队即将新元素加在队列的尾,元素出队即将队首元素取出,它后一个作为新的队首。
思路:
对完全二叉树最重要的定义就是叶子节点只能出现在最下层和次下层,所以我们想到可以使用队列辅助进行层次遍历——从上到下遍历所有层,每层从左到右,只有次下层和最下层才有叶子节点,其他层出现叶子节点就意味着不是完全二叉树。
具体做法:
- step 1:先判断空树一定是完全二叉树。
- step 2:初始化一个队列辅助层次遍历,将根节点加入。
- step 3:逐渐从队列中弹出元素访问节点,如果遇到某个节点为空,进行标记,代表到了完全二叉树的最下层,若是后续还有访问,则说明提前出现了叶子节点,不符合完全二叉树的性质。
- step 4:否则,继续加入左右子节点进入队列排队,等待访问。
图示:
Java代码实现:
import java.util.*;
public class Solution {
public boolean isCompleteTree (TreeNode root) {
//空树一定是完全二叉树
if(root == null)
return true;
//辅助队列
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
TreeNode cur;
//定义一个首次出现的标记位
boolean notComplete = false;
while(!queue.isEmpty()){
cur = queue.poll();
//标记第一次遇到空节点
if(cur == null){
notComplete = true;
continue;
}
//后续访问已经遇到空节点了,说明经过了叶子
if(notComplete)
return false;
queue.offer(cur.left);
queue.offer(cur.right);
}
return true;
}
}
C++代码实现:
class Solution {
public:
bool isCompleteTree(TreeNode* root) {
//空树一定是完全二叉树
if(root == NULL)
return true;
queue<TreeNode*> q;
//根节点先访问
q.push(root);
//定义一个首次出现的标记位
bool flag = false;
//层次遍历
while(!q.empty()){
int sz = q.size();
for (int i = 0; i < sz; i++) {
TreeNode* cur = q.front();
q.pop();
//标记第一次遇到空节点
if (cur == NULL)
flag = true;
else{
//后续访问已经遇到空节点了,说明经过了叶子
if (flag) return false;
q.push(cur->left);
q.push(cur->right);
}
}
}
return true;
}
};
Python代码实现
import queue
class Solution:
def isCompleteTree(self , root: TreeNode) -> bool:
# 空树一定是完全二叉树
if not root:
return True
q = queue.Queue()
# 根节点先访问
q.put(root)
# 定义一个首次出现的标记位
flag = False
# 层次遍历
while not q.empty():
sz = q.qsize()
for i in range(sz):
cur = q.get()
# 标记第一次遇到空节点
if not cur:
flag = True
else:
# 后续访问已经遇到空节点了,说明经过了叶子
if flag:
return False
q.put(cur.left)
q.put(cur.right)
return True
复杂度分析:
- 时间复杂度:,其中为二叉树节点数,层次遍历最坏情况下遍历每一个节点
- 空间复杂度:,最坏情况下,层次队列的最大空间为