LeetCode 二叉樹和遞歸專題 1:從二叉樹的角度看遞歸

我們開始介紹“二叉樹和遞歸”听盖。遞歸,是使用計算機(jī)解決問題的一種重要的思考方式幢痘。而二叉樹由于其天然的遞歸結(jié)構(gòu)唬格,使得基于二叉樹的算法家破,均擁有著遞歸性質(zhì)颜说。使用二叉樹,是研究學(xué)習(xí)遞歸算法的最佳入門方式汰聋。在這一章里门粪,我們就來看一看二叉樹中的遞歸算法。

在前面知識的學(xué)習(xí)中烹困,我們看到了在基礎(chǔ)算法以及系統(tǒng)設(shè)計中都用到了遞歸玄妈。深度優(yōu)先遍歷中也用到了遞歸。從這一部分開始髓梅,我們從另一個視角看遞歸拟蜻。

從二叉樹的角度看遞歸

二叉樹天然具有遞歸的性質(zhì)。二叉樹的定義就是用二叉樹定義二叉樹枯饿。對于二叉樹的定義來說酝锅,應(yīng)該補(bǔ)充一點(diǎn):空是一棵二叉樹。

下面奢方,我們來觀察一個二叉樹的前序遍歷的遞歸方法搔扁。

Java 代碼:

public void preorder(TreeNode node){
    if(node != null){
        System.out.print(node.val);
        preorder(node.left);
        preorder(node.right);
    } 
}

注意:這里 System.out.print(node.val); 這一行代碼表示“處理這個結(jié)點(diǎn)的邏輯”。

在這里蟋字,我們先強(qiáng)調(diào)編寫遞歸函數(shù)的第 1 個注意事項:首先明確這個函數(shù)要表達(dá)的任務(wù)(邏輯)稿蹲,明確參數(shù)的定義和返回值的意義。

接下來鹊奖,我們將上面的代碼改造一下苛聘。

Java 代碼:

public void preorder(TreeNode node){
    
    // 先寫遞歸終止條件
    if(node == null){
        return;
    } 
    
    // 再寫遞歸過程
    System.out.print(node.val);
    preorder(node.left);
    preorder(node.right);
}

其實(shí)這樣的寫法更像遞歸結(jié)構(gòu),因?yàn)閷τ谖覀兌x的每一個遞歸函數(shù)來說忠聚,應(yīng)該包含下面兩個部分:1焰盗、遞歸終止條件;2咒林、設(shè)立遞歸過程熬拒,定義清楚函數(shù)的語義。

這就是我們編寫一個遞歸函數(shù)應(yīng)該注意的第 2 件事情垫竞。

接下來澎粟,我們再看一個方法:在一個二叉樹中查看是否存在一個鍵值蛀序。寫這個遞歸方法的步驟:想想(1)遞歸終止條件是什么?(2)遞歸過程是什么活烙?參數(shù) Node node 是什么意思徐裸?我們這里定義的參數(shù) Node 對象,是在以 node 為根結(jié)點(diǎn)的二叉樹中尋找指定的 key啸盏。于是重贺,我們的邏輯是:如果 node 本身不是我們要找的結(jié)點(diǎn)的話,我們繼續(xù)在 node 的左孩子和右孩子中繼續(xù)查找回懦。

Java 代碼:

public boolean contain(Node node, int key) {
    // 首先我們要處理遞歸到底的情況
    if (node == null) {
        return false;
    }
    
    if (key == node.key) {
        return true;
    } 
    
    if (contain(node.left, key) || contain(node.right, key) {
        return true;
    } 
    return false;
}

那么气笙,我們可以再想想如何釋放以 Node 為根結(jié)點(diǎn)的二叉樹?

例題

例1:LeetCode 第 104 題:求一棵二叉樹的最大深度怯晕。

傳送門:英文網(wǎng)址:104. Maximum Depth of Binary Tree 潜圃,中文網(wǎng)址:104. 二叉樹的最大深度

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

二叉樹的深度為根節(jié)點(diǎn)到最遠(yuǎn)葉子節(jié)點(diǎn)的最長路徑上的節(jié)點(diǎn)數(shù)。

說明: 葉子節(jié)點(diǎn)是指沒有子節(jié)點(diǎn)的節(jié)點(diǎn)吧凉。

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

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

關(guān)鍵:要能看出這道題本質(zhì)上是二叉樹的后序遍歷阀捅。

Python 代碼:

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


class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if root is None:
            return 0
        # 先計算左右子樹胀瞪,然后再計算自己,這是后序遍歷
        l_sub_tree_depth = self.maxDepth(root.left)
        r_sub_tree_depth = self.maxDepth(root.right)
        return max(l_sub_tree_depth, r_sub_tree_depth) + 1

Python 代碼:把上面的遞歸改成循環(huán)

class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if root is None:
            return 0
        depth = 0
        stack = [(1, root)]
        while stack:
            cur_depth, node = stack.pop()
            depth = max(depth, cur_depth)
            if node.left:
                stack.append((cur_depth + 1, node.left))
            if node.right:
                stack.append((cur_depth + 1, node.right))
        return depth

說明:這個寫法看一看就好也搓,感覺沒啥意思赏廓。

感覺遞歸調(diào)用就像什么都沒有做一樣。通過這個例子傍妒,我們來理解一下(1)(2)這兩個步驟的具體應(yīng)用幔摸。這讓我想起了八皇后問題。

還可以使用 DFS 和 BFS 完成這個問題颤练。首先 BFS 我覺得思路更直接一些既忆,代碼也是有套路的。

Python 代碼:

class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if root is None:
            return 0
        depth = 0
        queue = [root]
        while queue:
            size = len(queue)
            depth += 1
            for _ in range(size):
                first = queue.pop(0)
                if first.left:
                    queue.append(first.left)
                if first.right:
                    queue.append(first.right)
        return depth

Python 代碼:使用 DFS

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

# 求一棵二叉樹的最大深度嗦玖。
# 要能看出這道題本質(zhì)上是二叉樹的后序遍歷患雇。


class Solution(object):

    def __init__(self):
        self.max_depth = 0

    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if root is None:
            return 0
        self.__dfs(root, 0)
        return self.max_depth

    def __dfs(self, node, depth):
        if node is None:
            return
        depth += 1
        if node.left is None and node.right is None:
            # 到葉子結(jié)點(diǎn)了,可以結(jié)算了
            self.max_depth = max(self.max_depth, depth)
            return
        if node.left:
            self.__dfs(node.left, depth)
        if node.right:
            self.__dfs(node.right, depth)
  • 復(fù)習(xí)和二叉樹相關(guān)的所有操作宇挫。

練習(xí)

練習(xí)1:LeetCode 第 111 題:求一棵二叉樹的最小深度苛吱。

傳送門:英文網(wǎng)址:111. Minimum Depth of Binary Tree ,中文網(wǎng)址:111. 二叉樹的最小深度 器瘪。

給定一個二叉樹翠储,找出其最小深度绘雁。

最小深度是從根節(jié)點(diǎn)到最近葉子節(jié)點(diǎn)的最短路徑上的節(jié)點(diǎn)數(shù)量。

說明: 葉子節(jié)點(diǎn)是指沒有子節(jié)點(diǎn)的節(jié)點(diǎn)援所。

示例:

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

    3
   / \
  9  20
    /  \
   15   7

返回它的最小深度 2.

分析:即求一棵二叉樹從根結(jié)點(diǎn)到葉子結(jié)點(diǎn)的最短路徑的長度庐舟。

  • 這個問題里面有小的陷阱。

  • 我們在思考遞歸終止條件的時候住拭,有的時候可能會存在陷阱挪略。

LeetCode 第 111 題:求一棵二叉樹的最小深度

這道題,我第一次做是想當(dāng)然滔岳,順著第 104 題把最大改成最小杠娱,但是要注意到上圖 4 那個結(jié)點(diǎn),4 的左孩子為空澈蟆,返回 0 卓研,右孩子為 9趴俘,返回2奏赘,按照我們的邏輯就返回 0 ,顯然是錯誤的磨淌,所以要針對左右孩子有一個為空的時候疲憋,做出分類判斷梁只。

Python 代碼:

# Definition for a binary tree node.
# 111. 二叉樹的最小深度
# 給定一個二叉樹,找出其最小深度搪锣。
#
# 最小深度是從根結(jié)點(diǎn)到最近葉子結(jié)點(diǎn)的最短路徑上的結(jié)點(diǎn)數(shù)量。
#
# 說明: 葉子結(jié)點(diǎn)是指沒有子結(jié)點(diǎn)的結(jié)點(diǎn)构舟。


class TreeNode(object):
    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


class Solution(object):
    def minDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """

        if root is None:
            return 0

        if root.left is None:
            return 1 + self.minDepth(root.right)

        if root.right is None:
            return 1 + self.minDepth(root.left)

        return 1 + min(self.minDepth(root.left), self.minDepth(root.right))

Python 代碼:使用 BFS:==使用層序遍歷灰追,我感覺更直接一些,因?yàn)橹灰獟叩饺~子結(jié)點(diǎn)狗超,就可以返回了==

class Solution(object):
    def minDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """

        if root is None:
            return 0
        depth = 0
        queue = [root]
        while queue:
            depth += 1
            size = len(queue)
            for _ in range(size):
                first = queue.pop(0)
                if first.left is None and first.right is None:
                    return depth
                if first.left:
                    queue.append(first.left)
                if first.right:
                    queue.append(first.right)

Python 代碼:使用 DFS

# Definition for a binary tree node.
# 111. 二叉樹的最小深度
# 給定一個二叉樹弹澎,找出其最小深度。
#
# 最小深度是從根結(jié)點(diǎn)到最近葉子結(jié)點(diǎn)的最短路徑上的結(jié)點(diǎn)數(shù)量努咐。
#
# 說明: 葉子結(jié)點(diǎn)是指沒有子結(jié)點(diǎn)的結(jié)點(diǎn)苦蒿。


class TreeNode(object):

    def __init__(self, x):
        self.val = x
        self.left = None
        self.right = None


class Solution(object):

    def __init__(self):
        self.min_depth = float("inf")

    def minDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """

        if root is None:
            return 0

        self.__dfs(root, 0)
        return self.min_depth

    def __dfs(self, node, depth):
        if node is None:
            return
        depth += 1
        if node.left is None and node.right is None:
            self.min_depth = min(self.min_depth, depth)
            return
        if node.left:
            self.__dfs(node.left, depth)
        if node.right:
            self.__dfs(node.right, depth)

(本節(jié)完)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市渗稍,隨后出現(xiàn)的幾起案子佩迟,更是在濱河造成了極大的恐慌溃肪,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件音五,死亡現(xiàn)場離奇詭異惫撰,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)躺涝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進(jìn)店門厨钻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坚嗜,你說我怎么就攤上這事夯膀。” “怎么了苍蔬?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵诱建,是天一觀的道長。 經(jīng)常有香客問我碟绑,道長俺猿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任格仲,我火速辦了婚禮押袍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凯肋。我一直安慰自己谊惭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布侮东。 她就那樣靜靜地躺著圈盔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悄雅。 梳的紋絲不亂的頭發(fā)上驱敲,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天,我揣著相機(jī)與錄音煤伟,去河邊找鬼。 笑死便锨,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的放案。 我是一名探鬼主播,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼掸冤,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了铅匹?” 一聲冷哼從身側(cè)響起饺藤,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤涕俗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后萌抵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體元镀,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年沐兰,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔽挠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澳淑。...
    茶點(diǎn)故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡杠巡,死狀恐怖雇寇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锨侯,我是刑警寧澤,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布叁怪,位于F島的核電站奕谭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏血柳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一攀隔、第九天 我趴在偏房一處隱蔽的房頂上張望栖榨。 院中可真熱鬧,春花似錦满粗、人聲如沸愚争。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鞍陨。三九已至,卻和暖如春缭裆,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背澈驼。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工缝其, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徘六,地道東北人。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓假残,卻偏偏與公主長得像,于是被迫代替她去往敵國和親阳惹。 傳聞我的和親對象是個殘疾皇子眶俩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,747評論 2 361

推薦閱讀更多精彩內(nèi)容