圖解精選 TOP 面試題 002 | 104. 二叉樹的最大深度

airland發表於2021-09-09

作者:
題解專案:
程式設計拯救世界(ID: CodeWarrior_):專注於程式設計基礎與服務端研發。

題目描述

原題連結:

給定一個二叉樹,找出其最大深度。

二叉樹的深度為根節點到最遠葉子節點的最長路徑上的節點數。

說明:葉子節點是指沒有子節點的節點。

示例:

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

    3
   / 
  9  20
    /  
   15   7

返回它的最大深度 3 。

解題思路

題目要求求出二叉樹的最大深度,我們知道,每個節點的深度與它左右子樹的深度有關,且等於其左右子樹最大深度值加上 1,可以寫作:

maxDepth(root) = max(maxDepth(root.left), maxDepth(root.right)) + 1

[3,9,20,null,null,15,7] 為例,根節點 3 的深度取決於它左右子樹的深度:

圖片描述

因其左右子樹深度尚不可知,我們需要對其一一求解。

先來看左子樹,即以 4 為根節點的子樹,因為它沒有左右子節點,所以深度為 1:

圖片描述

再來看以 20 為根節點的右子樹,同理,它的深度也取決於左右子樹的深度:

圖片描述

它的左子節點 15 與右子節點 7 的情況與上述節點 4 相同,左右子節點均為空,所以這兩個節點的深度也是 1。由此我們可以得出節點 20 的深度為 2,推導過程如下:

max(左子樹最大深度, 右子樹最大深度) + 1
=
max(1, 1) + 1
=
1 + 1
=
2

這樣一來,我們知道了所有子節點的深度,各節點深度如下:

圖片描述

由此可得根節點 3 的深度為:

max(左子樹最大深度, 右子樹最大深度) + 1
=
max(1, 2) + 1
=
2 + 1
=
3

上述推導過程整體如下:

maxDepth(3-root)
=
max(maxDepth(4-sub), maxDepth(20-sub)) + 1
=
max(1, max(maxDepth(15-sub), maxDepth(7-sub)) + 1) + 1
=
max(1, maxDepth(1, 1) + 1) + 1
=
max(1, 2) + 1
=
2 + 1
=
3

在推導過程中我們看到 maxDepth() 函式頻繁出現,即我們在頻繁地求取某節點的最大深度。由此可見,「求節點的最大深度」是該題的子問題,該題最直觀的解答方式是用遞迴求解。

遞迴設計

在遞迴演算法中,遞迴函式的設計非常重要,首先我們要先明確該函式的作用,然後再確定何時結束與何時呼叫該函式

明確函式作用

該函式的作用用一句話概括就是:計算節點的最大深度

  • 函式輸入:確定的節點
  • 函式輸出:該節點的最大深度

何時結束

當輸入的節點為空節點時,我們無需繼續計算其子樹的深度,此時可以直接結束遞迴函式,並返回空節點的深度為 0。

何時呼叫

當輸入節點為非空節點時,該節點的深度取決於其左右子樹的深度,即:

maxDepth(root) = max(maxDepth(root.left), maxDepth(root.right)) + 1

此時需要進行函式的遞迴呼叫。

具體實現

Python

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if root is None:
            return 0
        return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1

Golang

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func maxDepth(root *TreeNode) int {
    if root == nil {
        return 0
    }
    return max(maxDepth(root.Left), maxDepth(root.Right)) + 1
}

func max(a int, b int) int {
    if a > b {
        return a
    }
    return b
}

複雜度

假設節點的數量為 n。

時間複雜度

因為每個節點都需要被訪問一次,因此時間複雜度為 O(n)

空間複雜度

考慮到遞迴使用呼叫棧(call stack)的情況。

  • 最壞情況:樹完全不平衡。例如每個節點都只有右節點,此時將遞迴 n 次,需要保持呼叫棧的儲存為 O(n)
  • 最好情況:樹完全平衡。即樹的高度為 log(n),此時空間複雜度為 O(log(n))

總結一下

與樹相關的題目常用遞迴來解,對於遞迴而言,我們需要明確:

  1. 遞迴函式的用途
  2. 遞迴函式的結束條件
  3. 遞迴函式自身呼叫的時機

除此之外,在計算空間複雜度時,我們也要考慮到遞迴時呼叫棧的情況。

當然了,這道題還可以用迭代法來做,由於篇幅有限,就不在本篇敘述了。大家可以想想要怎麼用迭代法解答本題,我們下次再來細說~

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/964/viewspace-2824362/,如需轉載,請註明出處,否則將追究法律責任。

相關文章