前言
写这篇博客的原因是今天去面试了,结果有段时间没刷题【都是借口…好好刷题吧…】让手撕了一个判断是否是一颗完全二叉树的代码,挣扎了挺久还是没有写完整,所以回来整理一下,继续努力!
【注:以下所有代码实现中的结点定义如下:
public class Node {
int val;
Node left;
Node right;
Node parent;
Node() {}
Node(int val) {
this.val = val;
}
}
1.如何判断一棵树是一颗完全二叉树
如果一棵树是一颗完全二叉树,那么如果是一个非叶结点,它的左右孩子要么都存在,要么只有左孩子,并且它右边的兄弟结点都是叶子结点;如果是一个叶子结点,那它的兄弟结点也都是叶子结点。如下图就是一颗完全二叉树,当然满二叉树也是一颗完全二叉树咯~
代码实现思路:
如果是一个空树,是一颗完全二叉树,返回true;
使用层次遍历的思想,所以需要使用一个队列,每遍历一个结点,将其从队列头部弹出并将其左右孩子放入队列的最后;
设置一个boolean类型变量isLeaf标记是否已经遍历到叶子结点了(需要注意的是,完全二叉树按照层次遍历的方式,只要遍历到叶子结点,之后的结点一定都是叶子结点了,不明白的小伙伴看看图就明白啦~);
循环进行层次遍历,判断以下几个条件:
- 如果该结点有右孩子却没有左孩子 -> 直接返回false
- 如果isLeaf为true,如果当前结点有孩子 -> 直接返回false
- 如果当前结点有左孩子就加入队列
- 如果当前结点有右孩子就加入队列
- 如果该结点有左孩子却没有右孩子 -> isLeaf置true
代码实现如下:
public boolean isComplete(Node head) {
if(head == null)
return true;
Queue<Node> queue = new LinkedList<>();
boolean isLeaf = false;
Node left = null;
Node right = null;
queue.offer(head);
while(!queue.isEmpty()) {
head = queue.poll();
left = head.left;
right = head.right;
// 如果当前结点是叶子结点并且有孩子 或 有右孩子没有左孩子
if(isLeaf && (left != null || right != null) || left == null && right != null) {
return false;
}
if(left != null) {
queue.offer(left);
}
if(right != null) {
queue.offer(right);
}else {
// 当前结点是最后一个非叶结点(左不空右空)或当前是第一个叶子结点(左右都空)
isLeaf = true;
}
}
return true;
}
2.如何判断一棵树是否是一颗平衡二叉树
平衡二叉树则的所有结点的左右子树的深度差不大于1,相反,如果任意一个结点的左子树和右子树深度差值大于1,那么就不是一颗平衡二叉树。
代码实现思路:
通过递归的方式,递归就要重点考虑返回值以及递归结束条件(出口)
- 返回值:当前结点的深度,如果是叶子结点就返回1,如果是非叶结点就返回左右子树深度最大值+1;同时,还需要返回是否是平衡树,如果某个结点的子树已经不平衡了,那么以该结点作为根的树自然也是不平衡的。所以在需要定义一个返回值类型ReturnData,其中包含两个信息——以当前结点作为根结点的子树的深度和以当前结点作为根结点的子树是否是一个平衡二叉树;
- 递归的出口:叶子结点返回1。
代码实现:
// 返回值类型定义
public class ReturnData{
private boolean isB;
private int high;
public ReturnData(boolean isB, int high) {
this.isB = isB;
this.high = high;
}
}
// 判断是否是一个平衡二叉树
public ReturnData process(Node head) {
if(head == null) {
// 空树是平衡的
return new ReturnData(true, 0);
}
// 拿到左右子树的信息
ReturnData leftData = process(head.left);
if(!leftData.isB)
return new ReturnData(false, 0);
ReturnData rightData = process(head.right);
if(!rightData.isB)
return new ReturnData(false, 0);
// 如果左右子树的高度差大于1 -> 不是平衡二叉树
if(Math.abs(leftData.high - rightData.high) > 1)
return new ReturnData(false, 0);
return new ReturnData(true, Math.max(leftData.high, rightData.high) + 1);
}
3.如何判断一棵树是否是一颗二叉排序(搜索)树
二叉排序树中每个结点的大小大于左子树中的任何值,且小于右子树中的任何值。
代码实现思路:
从二叉搜索树的特点,并结合二叉树的遍历可以得出:二叉排序树的中序遍历是升序的。所以只需要在进行中序遍历的时候对当前结点的值和上一个值的大小进行比较就可以判断一棵树是不是一颗二叉排序树了。
代码实现:
// 非递归实现的中序遍历
public static boolean isSearched(Node head) {
if(head == null)
return false;
int preNodeVal = Integer.MIN_VALUE;
Stack<Node> stack = new Stack<>();
while(!stack.isEmpty() || head != null) {
if(head != null) {
stack.push(head);
head = head.left;
}else {
head = stack.pop();
if(preNodeVal > head.val) {
// 不是排序树
return false;
}
preNodeVal = head.val;
head = head.right;
}
}
return true;
}