剑指 Offer 55 - I. 二叉树的深度
题目描述
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
提示:
- 节点总数 <= 10000
BFS
关于求深度,也可以使用 BFS
按层进行遍历。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
int deepth = 0;
Queue<TreeNode> queue = new LinkedList<>();
if (root != null) queue.offer(root);
while (!queue.isEmpty()) {
// 按层遍历
deepth++;
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
}
return deepth;
}
}
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)。树平衡时,队列中存放 n / 2 n / 2 n/2 个节点。
DFS (后序)⭐️
思路
- 后序遍历整个树,并且需要对 左、右 子树的结果 进行逻辑判断,所以 递归函数需要
返回值类型 int
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0;
// 后序
int left = maxDepth(root.left); // 左
int right = maxDepth(root.right); // 右
// 根
return Math.max(left, right) + 1;
}
}
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( n ) O(n) O(n)。退化为链表时,占用栈深度为 n n n
DFS(前序)
上面
后序
其实是求节点 “高度” 的写法,而求节点 “深度” 通常是使用前序
遍历方式。
前序 DFS 求 “深度” 代码如下:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int res = 0;
public int maxDepth(TreeNode root) {
// 单独处理(避免null进入dfs)
if (root == null) {
return 0;
}
dfs(root, 1); // root不为null,深度至少为1
return res;
}
void dfs(TreeNode root, int deepth) {
// 遇到叶子则停止(避免污染写法,不会让null进入dfs)
if (root.left == null && root.right == null) {
res = Math.max(res, deepth);
return;
}
// 预防污染,避免null进入下层dfs
if (root.left != null) { // 左
dfs(root.left, deepth + 1);
}
if (root.right != null) { // 右
dfs(root.right, deepth + 1);
}
}
}
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度:
O
(
n
)
O(n)
O(n)。退化为链表时(所有节点都挂在
left
上时),占用栈深度为 n n n
剑指 Offer 55 - II. 平衡二叉树
题目描述
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
限制:
- 0 < = 树 的 结 点 个 数 < = 10000 0 <= 树的结点个数 <= 10000 0<=树的结点个数<=10000
DFS(前序)
此种方法较为容易想到,但是存在大量重复计算。
思路 🤔
- 使用前序遍历,判断当前节点所在子树是不是
AVL
- 若当前节点的左、右子树的深度相差超过
1
1
1,则必然不是
AVL
- 其中,求当节点所在子树深度的函数
getDeepth()
和 剑指 Offer 55 - I. 二叉树的深度 完全一致
- 其中,求当节点所在子树深度的函数
- 否则,则递归判断左、右子树是不是
AVL
- 若当前节点的左、右子树的深度相差超过
1
1
1,则必然不是
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
// 终止条件(空树也是AVL)
if (root == null) return true;
// 当前节点的左右子树的深度相差不超过1,则必然不是AVL
if (Math.abs(getDeepth(root.left) - getDeepth(root.right)) > 1) {
return false;
}
// 递归判断左、右子树是不是AVL
if (!isBalanced(root.left)) { // 如果左子树不是AVL,则不必判断右子树,立即返回false
return false;
}
if (!isBalanced(root.right)) {
return false;
}
return true;
// return isBalanced(root.left) && isBalanced(root.right); // 精简
}
// 求以root为根节点的树的最大深度
int getDeepth(TreeNode root) {
if (root == null) return 0;
int left = getDeepth(root.left); // 左
int right = getDeepth(root.right); // 右
return Math.max(left, right) + 1;
}
}
以上 “递归判断左、右子树是不是
AVL
” 部分的代码,可以精简如下:
return isBalanced(root.left) && isBalanced(root.right);
-
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)。使用前序判断是否为
AVL
时,存在大量重复计算- 当树为 “满二叉树” 时,递归层数为 l o n g n longn longn
- 每层所需时间复杂度为: O ( n ) O(n) O(n)
- 所以,总时间复杂度 = 每层所需时间复杂度 * 层数 = O ( n l o g n ) O(nlogn) O(nlogn)
-
空间复杂度: O ( n ) O(n) O(n)。退化为链表时,递归占用栈深度为 n n n
复杂度分析 参考 K佬题解
DFS(后序 + 剪枝) ⭐️
和 剑指 Offer 55 - I. 二叉树的深度 “
后序解法
”类似,但是这里要多加一个 “剪枝
” 处理
思路 🤔
- 仍然用 后序 求二叉树(以及每个子树)的深度
剪枝
:如果某个子树中左、右孩子高度大于 1 1 1,则整个树都不可能是AVL
,则需要剪去(提前return
即可)
class Solution {
public boolean isBalanced(TreeNode root) {
// 空树是 AVL
if (root == null) return true;
return getDeepth(root) != -1;
}
public int getDeepth(TreeNode root) {
if (root == null) return 0;
// 后序
int left = getDeepth(root.left); // 左
if (left == -1) return -1; // 剪枝
int right = getDeepth(root.right); // 右
if (right == -1) return -1; // 剪枝
if (Math.abs(left - right) > 1) { // 某个子树中左、右孩子高度大于1,则整个树都不可能是 AVL
return -1;
}
// 逻辑处理
int deepth = Math.max(left, right) + 1;
return deepth;
}
}
- 时间复杂度: O ( n ) O(n) O(n)。遍历树中所有节点时
- 空间复杂度:
O
(
n
)
O(n)
O(n)。退化为链表时,递归占用栈深度为
n
n
n