104.二叉树的最大深度
首先阅读题目要求将二叉树问题分为两类问题
第一类:是否一次遍历都够得出答案
第二类:能够通过分解问题计算出答案
该题需要找出二叉树的最大深度(根节点到最远叶子节点的最长路径上的节点数)
而该题目能够遍历一次二叉树得出答案
代码如下:
// 记录最大深度
int res = 0;
// 记录遍历到的节点的深度
int depth = 0;
// 主函数
int maxDepth(TreeNode root) {
traverse(root);
return res;
}
// 二叉树遍历框架
void traverse(TreeNode root) {
if (root == null) {
return;
}
// 前序位置
depth++;
if (root.left == null && root.right == null) {
// 到达叶子节点,更新最大深度
res = Math.max(res, depth);
}
traverse(root.left);
traverse(root.right);
// 后序位置
depth--;
}
如果将该题目问题分解问题进行解决
便是:
一课二叉树的最大深度遍历结果 = 根节点 + 左子树的最大深度遍历结果 / 右子树的最大深度遍历结果
代码如下:
// 定义:输入根节点,返回这棵二叉树的最大深度
int maxDepth(TreeNode root) {
if (root == null) {
return 0;
}
// 利用定义,计算左右子树的最大深度
int leftMax = maxDepth(root.left);
int rightMax = maxDepth(root.right);
// 整棵树的最大深度等于左右子树的最大深度取最大值,
// 然后再加上根节点自己
int res = Math.max(leftMax, rightMax) + 1;
return res;
}
111.二叉树的最小深度
利用同上的二叉树解决思路
但需要注意一个坑:
二叉树最小深度是从根节点到最近叶子节点的最短路径上的节点数量
如果根据最大深度来写出如下代码:
int leftDepth = minDepth(node.left);
int rightDepth = minDepth(node.right);
int result = 1 + Math.min(leftDepth, rightDepth);
return result;
导致如果这么求的话,没有左孩子的分支会算为最短深度。
所以,如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。
反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。
犯下图错误:
正确代码如下:
class Solution {
public int minDepth(TreeNode root) {
if(root == null) return 0;
if(root.left == null && root.right == null){
return 1;
}
int left = Integer.MAX_VALUE;
int right = Integer.MAX_VALUE;
// 当一个左子树为空,右不为空,这时并不是最低点
if(root.left != null){
left = minDepth(root.left);
}
// 当一个右子树为空,左不为空,这时并不是最低点
if(root.right != null){
right = minDepth(root.right);
}
return Math.min(left,right) + 1;
}
}
222.完全二叉树的节点个数
首先这道题由于需要求节点个数,遍历一遍便可以得出答案
我写出以下代码:(利用层序遍历)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int countNodes(TreeNode root) {
int res = 0;
if(root == null){
return res;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode cur = stack.pop();
res++;
if(cur.left != null){
stack.push(cur.left);
}
if(cur.right != null){
stack.push(cur.right);
}
}
return res;
}
}
但时间复杂度很高 O(n)
做出优化
做出以下优化:
由于一颗完全二叉树,至少有一颗是满二叉树,另一颗有可能为普通二叉树
如果是一颗普通二叉树,只需要如下遍历一边即可,时间复杂度为 O(n)
public int countNodes(TreeNode root) {
if (root == null) return 0;
return 1 + countNodes(root.left) + countNodes(root.right);
}
那如果是一棵满二叉树,节点总数就和树的高度呈指数关系:
public int countNodes(TreeNode root) {
int h = 0;
// 计算树的高度
while (root != null) {
root = root.left;
h++;
}
// 节点总数就是 2^h - 1
return (int)Math.pow(2, h) - 1;
}
而完全二叉树就是普通二叉树与满二叉树的结合版
代码如下:
class Solution {
public int countNodes(TreeNode root) {
if(root == null){
return 0;
}
TreeNode left = root;
TreeNode right = root;
// 沿最左侧和最右侧分别计算高度
int leftDepth = 0, rightDepth = 0;
while(left != null){
left = left.left;
leftDepth++;
}
System.out.println(leftDepth);
while(right != null){
right = right.right;
rightDepth++;
}
// 如果左右侧计算的高度相同,则是一棵满二叉树
if(leftDepth == rightDepth){
return (int)Math.pow(2,leftDepth) - 1;
}
// 如果左右侧的高度不同,则按照普通二叉树的逻辑计算
return countNodes(root.left) + countNodes(root.right) + 1;
}
}