博客 / 詳情

返回

劍指offer-39、平衡⼆叉樹

題⽬描述

輸⼊⼀棵節點數為 n ⼆叉樹,判斷該⼆叉樹是否是平衡⼆叉樹。

在這⾥,我們只需要考慮其平衡性,不需要考慮其是不是排序⼆叉樹

平衡⼆叉樹( Balanced Binary Tree ),具有以下性質:它是⼀棵空樹或它的左右兩個⼦樹的⾼度差的絕對值不超過 1 ,並且左右兩個⼦樹都是⼀棵平衡⼆叉樹。

樣例解釋:

思路及解答

自頂向下遞歸(基礎解法)

平衡樹意味着我們需要對⽐任何在同⼀個根下的左右⼦樹的⾼度差,還記得之前我們計算樹的⾼度麼,使⽤遞歸⽅式來解決,其實這道題與算⾼度差不多,只是兩邊⾼度需要算出⼀個差值。

算法的主要思想:

  • 不斷對⽐每兩個節點的左右⼦樹的最⼤⾼度差,注意取差的絕對值,需要⼩於等於1
  • 對⽐完左右⼦樹之後,需要遞歸左⼦樹以及右⼦樹進⾏分別判斷,都滿⾜才是平衡樹
public class Solution79 {
    public boolean IsBalanced_Solution(TreeNode root) {
        if (root == null) return true;
        // 當前左右⼦樹是否平衡以及左右⼦樹分別是否平衡
        return Math.abs(depth(root.left) - depth(root.right)) <= 1 &&
            IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
    }
    private int depth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        // 遞歸獲取深度
        return Math.max(depth(root.left), depth(root.right)) + 1;
    }
}
  • 時間複雜度 O(nlogn) :最差情況下,需要遍歷樹所有節點判斷是否平衡,需要O(n)。但是判斷每個節點最⼤⾼度需要遞歸左右⼦樹,需要佔⽤ O(log2n) ,所以總共佔⽤ O(Nlog2n)
  • 空間複雜度 O(n) :最差情況下,也就是樹退化為鏈表時,遞歸需要使⽤ O(n) 的棧空間,嚴格意義上遞歸棧也需要空間。

自底向上遞歸(優化解法)

上⾯的計算,仔細觀察就會發現會有很多重複計算的過程,⽐如下⾯的數,計算⼦樹深度的時候,計算 1 的左⼦樹,和計算 2 的,基本都重複了。

應該如何避免這種重複計算呢?前⾯的是⾃頂向下的⽅式,因為每個節點都得把⼦樹計算⼀遍才需要重複,如果我們從下往上計算,那不就避免了重複計算。後序遍歷,先判斷子樹平衡性,再判斷當前節點,對⽐邏輯如下:

  • 如果當前節點為空,⾼度為0
  • 如果當前節點的左⼦樹的⾼度為-1,那麼説明不平衡,否則,需要計算右⼦樹⾼度,同樣需要不等於-1,如果兩者的差不符合⼩於等於1,那麼説明它們不平衡,返回-1。通過這樣 -1 異常值就會⼀路返回,到最初的調⽤處,得到不平衡的結果。
public class Solution79 {
    public boolean IsBalanced_Solution(TreeNode root) {
        // 空樹
        if (root == null) {
            return true;
        }
        return getHeight(root) != -1;
    }
    public int getHeight(TreeNode root) {
        if (root == null) {
            return 0;//空節點高度為0
        }
        // 左⼦樹的⾼度
        int left = getHeight(root.left);
        if (left < 0) {
            return -1;//左子樹不平衡,直接返回
        }
        // 右⼦樹的⾼度
        int right = getHeight(root.right);
        if (right < 0) {
            return -1;//右子樹不平衡,直接返回
        }
        
        // 檢查當前節點是否平衡
        return Math.abs(left - right) > 1 ? -1 : 1 + Math.max(left, right);
    }
}
  • 時間複雜度 O(n) :每個節點計算⼀次
  • 空間複雜度 O(n) :遞歸需要使⽤額外堆棧空間

筆試面試時,建議首選這個方式,效率最優,代碼簡潔

封裝信息法(面向對象思路)

通過自定義類同時返回高度和平衡信息,代碼結構更清晰。

class BalanceInfo {
    boolean isBalanced;
    int height;
    
    BalanceInfo(boolean isBalanced, int height) {
        this.isBalanced = isBalanced;
        this.height = height;
    }
}

public class Solution {

    public boolean isBalanced(TreeNode root) {
        return checkBalance(root).isBalanced;
    }
    
    private BalanceInfo checkBalance(TreeNode node) {
        if (node == null) {
            return new BalanceInfo(true, 0); // 空節點平衡且高度為0
        }
        
        // 遞歸檢查左右子樹
        BalanceInfo leftInfo = checkBalance(node.left);
        BalanceInfo rightInfo = checkBalance(node.right);
        
        // 如果子樹不平衡,直接返回
        if (!leftInfo.isBalanced || !rightInfo.isBalanced) {
            return new BalanceInfo(false, -1); // 高度值此時不重要
        }
        
        // 檢查當前節點是否平衡
        if (Math.abs(leftInfo.height - rightInfo.height) > 1) {
            return new BalanceInfo(false, -1);
        }
        
        // 返回當前節點的平衡信息和高高度
        int currentHeight = Math.max(leftInfo.height, rightInfo.height) + 1;
        return new BalanceInfo(true, currentHeight);
    }
}
user avatar hsiang 頭像
1 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.