1. 程式人生 > >LeetCode演算法題-Maximum Depth of Binary Tree

LeetCode演算法題-Maximum Depth of Binary Tree

這是悅樂書的第164次更新,第166篇原創

01 看題和準備

今天介紹的是LeetCode演算法題中Easy級別的第23題(順位題號是104)。給定二叉樹,找到它的最大深度。最大深度是從根節點到最遠葉節點的最長路徑上的節點數。葉子是沒有子節點的節點。

例如:給定二叉樹[3,9,20,null,null,15,7],

    3
   / \
  9  20
     / \
    15  7

返回其深度= 3。

本次解題使用的開發工具是eclipse,jdk使用的版本是1.8,環境是win7 64位系統,使用Java語言編寫和測試。

02 第一種解法

最大深度是根節點到最遠的葉子節點路徑上包含的節點數,以上面的二叉樹為例,最長路徑有兩條:3->20->15,3->20->7,這兩條路徑上的節點數都是3個,所以最後得出其深度是3的結論。

特殊情況一:當傳入的二叉樹為空時,它沒有任何節點,它的深度是0。

特殊情況二:只有根節點的時候,它的深度是1,只有它自身一個節點。

正常情況:我們可以一步一步試著推導下,一個簡單的二叉樹深度計算過程,還是以上面的二叉樹為例。

從根節點開始,此時節點數為1,因為只有它一個節點。

進入根節點的子節點,此時最長路徑就是計算9這個左子節點和20這個右子節點的最長路徑,顯然9是一個葉子節點,只有本身一個節點;而20擁有自己的子節點,此時就需要算出從20出發的最長路徑。

20有左子節點15,右子節點7,這時需要繼續計算15和7的最長路徑,而15和7都是葉子節點,所以節點數只有1,再加上20節點本身這個屬於根節點的子節點,20節點的最長路徑就是2。

同為3根節點的兩個子節點9、20,子節點9的最長路徑上節點數為1,子節點20的最長路徑上節點數為2,取最大值後,再加上根節點自身的節點數,1+2=3,3就是最長路徑上的節點數,也就是該二叉樹的深度。

分析到這裡,我們知道了,要計算從根節點到最遠葉子節點的節點個數,就需要先計算其左右子節點的最長路徑,而要計算左右子節點的最長路徑,就需要計算他們自身下面的左右子節點的最長路徑了,對此我們可以使用遞迴,算完最下面葉子節點的個數後,再層層往上求其最大值。

public int maxDepth(TreeNode root) {
    if (root == null) {
        return 0;
    }
    int left = maxDepth(root.left);
    int right = maxDepth(root.right);
    return 1 + Math.max(left, right);
}


03 第二種解法

既然可以使用遞迴,那我們也可以試著使用遍歷的方法。從根節點開始,自頂向下遍歷子節點,對此使用佇列來臨時儲存每次遍歷的子節點,遍歷完一層子節點就加1,直到所有子節點都遍歷完。

public int maxDepth2(TreeNode root) {
    if (root == null) {
        return 0;
    }
    int depth = 0;
    Queue<TreeNode> q = new LinkedList<>();
    q.offer(root);
    while (!q.isEmpty()) {
        Queue<TreeNode> tem = new LinkedList<>();
        while(!q.isEmpty()){
            TreeNode node = q.poll();
            if (node.left != null) {
                tem.offer(node.left);
            }
            if (node.right != null) {
                tem.offer(node.right);
            }
        }
        q = tem;
        depth++;
    }
    return depth;
}

特殊情況還是和第一種解法一樣,內層while迴圈是遍歷每層的子節點。

04 第三種解法

第二種解法的內層迴圈那裡,我們使用了新的佇列來接收每次迴圈要進入的下一層節點資料,是否可以改動下,使其更加的簡潔?這裡我們使用佇列的大小來控制它,從根節點開始,進入佇列後,佇列的size為1,開始進入內層迴圈,內層迴圈走了一次後,佇列裡又多了兩個子節點,此時的size為2,然後繼續開始內層迴圈,直到所有的子節點遍歷完。

public int maxDepth3(TreeNode root) {
    if (root == null) {
        return 0;
    }
    int depth = 0;
    Queue<TreeNode> q = new LinkedList<>();
    q.offer(root);
    while (!q.isEmpty()) {
        int n = q.size();
        while(n-- > 0){
            TreeNode node = q.poll();
            if (node.left != null) {
                q.offer(node.left);
            }
            if (node.right != null) {
                q.offer(node.right);
            }
        }
        depth++;
    }
    return depth;
}


05 驗證與測試

對於上面的兩種解法,使用了一個簡單的二叉樹做了資料測試。

public static void main(String[] args) {
    Easy_104_MaximumDepthOfBinaryTree instance = new Easy_104_MaximumDepthOfBinaryTree();
    TreeNode t = new TreeNode(1);
    TreeNode t2 = new TreeNode(2);
    TreeNode t3 = new TreeNode(3);
    TreeNode t4 = new TreeNode(4);
    TreeNode t5 = new TreeNode(5);
    TreeNode t6 = new TreeNode(6);
    TreeNode t7 = new TreeNode(7);
    TreeNode t8 = new TreeNode(8);
    t.left = t2;
    t.right = t3;
    t2.left = t4;
    t2.right = t5;
    t3.left = t6;
    t3.right = t7;
    t7.left = t8;
    long start = System.nanoTime();
    int result = instance.maxDepth(t);
    long end = System.nanoTime();
    System.out.println("maxDepth---輸出:"+result+" , 用時:"+(end-start)/1000+"微秒");
    long start2 = System.nanoTime();
    int result2 = instance.maxDepth2(t);
    long end2 = System.nanoTime();
    System.out.println("maxDepth2---輸出:"+result2+" , 用時:"+(end2-start2)/1000+"微秒");
    long start3 = System.nanoTime();
    int result3 = instance.maxDepth3(t);
    long end3 = System.nanoTime();
    System.out.println("maxDepth3---輸出:"+result3+" , 用時:"+(end3-start3)/1000+"微秒");
}

測試結果如下:

maxDepth---輸出:4 , 用時:23微秒
maxDepth2---輸出:4 , 用時:586微秒
maxDepth3---輸出:4 , 用時:16微秒

從測試的結果可以看出,遍歷和遞迴都是可以解決問題的,第二種解法因為每進一次迴圈都要建立新的物件,這對記憶體和執行時間都是不小的消耗,經過優化後第三種解法更加適合遍歷使用。

06 小結

以上就是全部內容,如果大家有什麼好的解法思路、建議或者其他問題,可以下方留言交流,點贊、留言、轉發就是對我最大的回報和支援!