话不多说,在面试中遇到过,一脸蒙蔽,被虐出翔…以下所述,仅仅是手撕代码时候使用,若是需要在线编程,可以根据该思路编写对应AC代码。
-
如何判断一个二叉树是否平衡?要解决这个问题,首先要知道什么是平衡二叉树。
-
平衡二叉树定义如下:
- 首先,是一个二叉树,且每个节点的左右子树的高度差的绝对值不超过1。
- 其次,空树是平衡的。
-
试想要实现该代码,需要怎么记录其中的高度差、当前节点是否平衡、以及怎么统计每个节点的左右子树的高度,话不多说,解决思路如下:
-
高度差和左右子树高度,其实我们只需要记录一个,除此之外,还需记录当前节点是否平衡,那么总共有两个变量,数据结构如下:
static class returnDate { // 是否平衡 public boolean isBlance; // 树高度 public int h; public returnDate(boolean isBlance, int h) { this.isBlance = isBlance; this.h = h; } }
-
确定返回数据结构后,我们可以使用深度遍历,判断每个节点是否平衡。
- 使用前序遍历,遍历二叉树所有节点。
public static returnDate process(TreeNode head) { // 空=平衡 if(head == null) { return new returnDate(true, 0); } returnDate leftDate = process(head.left); // 判断左子树是否平衡 if(!leftDate.isBlance) { return new returnDate(false, 0); } returnDate rightDate = process(head.right); // 判断右子树是否平衡 if(!rightDate.isBlance) { return new returnDate(false, 0); } // 高度差===判断平衡 if(Math.abs(leftDate.h - rightDate.h) > 1) { return new returnDate(false, 0); } // 当前结点平衡,返回高度 // 当前左右子树最高加上当前节点 return new returnDate(true, Math.max(leftDate.h, rightDate.h) + 1); }
-
-
-
平衡二叉树已经解决,那么我再看怎么判断一个二叉树是BST树。
- BST,即binary search tree二叉搜索树,其性质如下:
- 若任意结点的左子树不空,则左子树上的所有节点均小于其根节点。
- 若任意结点的右子树不空,则右子树上的所有节点均大于其根节点。
- 任意节点的左、右子树皆为BST树。
- 没有值相等的数,即数值不重复。
- 其实,有很多种方法解决该问题,笔者只提供其中一种。
- 解决思路:如果某二叉树的中序遍历是升序的,则该树是BST树。
- 中序遍历有递归和非递归两种实现方法,在此笔者选择非递归实现。
public static boolean isBST(TreeNode head) { if(head == null) return true; Stack<TreeNode> stack = new Stack<>(); TreeNode cur = head; // 记录状态 boolean is_bst = true; // 前一个节点 int pre = Integer.MIN_VALUE; while(!stack.isEmpty() || cur != null) { if(cur != null) { stack.push(cur); cur = cur.left; } else { cur = stack.pop(); // 迭代 if(is_bst) { // 初始化前一个节点 pre = cur.val; is_bst = false; } else if(pre > cur.val) { return false; } else { // 记录前一个节点 pre = cur.val; } cur = cur.right; } } return true; }
- BST,即binary search tree二叉搜索树,其性质如下:
-
最后一个判断完全二叉树逻辑比较复杂,万事开头难,先从简单走起。
-
完全二叉树的定义:
-
对于深度为K的,N个节点 二叉树,当且仅当其每一个节点都与深度为K的满二叉树中编号从1到N的结点。
-
其实,看了其定义,笔者也看不懂。
-
简单点,要画一个完全二叉树,必须先画根节点,再画左结点,最后画右结点,画的时候顺序必须严格一致。
-
-
解决思路:
- 先确定遍历方式:笔者使用层序遍历
- 明确违反完全二叉树条件:
- 如果一个节点,有右子结点,没有左子结点,则一定不是完全二叉树。
- 如果一个节点左右子节点不全,即有左没右或两个都没有,若出现该状态,则接下来的节点都必须是叶子节点。
/** * 判断一个树是否是完全二叉树 * 思路, 按层遍历 * Q1,如果一个节点有右结点,而没有左结点,一定不是。 * Q2,如果一个节点左右不全, 即有左没右,或左右都没有, 该节点之后出现的结点,必须是叶子结点。 * @author commonsstring@gmail.com * */ public static boolean isCBT(TreeNode head) { if(head == null) return true; Queue<TreeNode> queue = new LinkedList<>(); queue.offer(head); // 左右孩子 TreeNode left = null; TreeNode right = null; // 开启状Q2 boolean flag = false; while(!queue.isEmpty()) { TreeNode current = queue.poll(); left = current.left; right = current.right; // 开启状态后, 节点不会存在左右子节点 if(flag && (left != null || right != null) || (left == null && right != null)) { return false; } // 层序遍历, 入队逻辑 if(left != null) { queue.offer(left); } if(right != null) { queue.offer(right); } // 遇到Q2, 开启状态, 即以后的每一个节点都是叶子节点 if(left == null || right == null) { flag = true; } } return true; }
-
-
最后,只要记住每种二叉树的性质,代码很好写,仅是效率的差别而已。