LeetCode 110. 平衡二叉树 剪枝题解,先有暴力递归解,再有后序遍历优化。

题目

110. 平衡二叉树

https://leetcode.cn/problems/balanced-binary-tree/

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

从上往下求深度

思路

树形遍历,我最先想到的是DFS,同时题干给出了定义。

不难写出定义判定公式:

1
Math.abs(depth(root.left, 0) - depth(root.right, 0)) <= 1

同时平衡是否同样适用于父节点的左右子树。

复杂度分析

时间

此解法需递归计算子树深度,同时会在每个节点上重复遍历。复杂度O(n^2)

空间

递归栈为主要空间开销,树退化为链表时,栈正好为节点数长度,空间复杂度O(n)

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
static class UpToDown {

      /**
      * 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
      *
      * @return abs(h ( root.left)-h(root.right))<=1
      */
      public boolean isBalanced(TreeNode root) {
            if (root == null) {
                  return true;
            }
            return Math.abs(depth(root.left, 0) - depth(root.right, 0)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
      }

      private int depth(TreeNode node, int h) {
            if (node == null) {
                  return h;
            }
            return Math.max(depth(node.left, h + 1), depth(node.right, h + 1));
      }
}

从下往上求高度

思路

解法1可以看到时间复杂度很高。因为有重复计算。

树形遍历,一定有办法剪枝。

我们可以学习后序遍历的思路,每次先处理子树,最后处理当前父节点,这样每个父节点实际只会计算一次高度。 同时在某一层如果已经判断左右子树不平衡,整个函数也可以提前返回。

复杂度分析

时间

后序遍历,整个树只会下去之后再上去一遍。复杂度O(n)

空间

同解法1空间开销,空间复杂度O(n)

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static class DownToUp {
      /*
      * 从下往上算
      * h 函数负责计算当前节点高度
      * -1表示非平衡二叉树的高度,作为特判
      */

      public boolean isBalanced(TreeNode root) {
            return h(root) != -1;
      }

      /**
      * 从下往上处理,即后序遍历,先处理左右子树,最后处理当前节点
      */
      private int h(TreeNode parent) {
            if (parent == null) {
                  return 0;
            }
            int hLeft = h(parent.left);
            if (hLeft == -1) {
                  return -1;
            }
            int hRight = h(parent.right);
            if (hRight == -1) {
                  return -1;
            }
            if (Math.abs(hLeft - hRight) > 1) {
                  return -1;
            }
            // 父节点高度为左右子树高度较大值+1
            return Math.max(hLeft, hRight) + 1;
      }
}

可以看到解法2效率更高,解法1则代码更加简洁。