LeetCode 二叉樹(shù)和遞歸專(zhuān)題 1:從二叉樹(shù)的角度看遞歸

我們開(kāi)始介紹“二叉樹(shù)和遞歸”。遞歸,是使用計(jì)算機(jī)解決問(wèn)題的一種重要的思考方式。而二叉樹(shù)由于其天然的遞歸結(jié)構(gòu),使得基于二叉樹(shù)的算法,均擁有著遞歸性質(zhì)。使用二叉樹(shù),是研究學(xué)習(xí)遞歸算法的最佳入門(mén)方式。在這一章里,我們就來(lái)看一看二叉樹(shù)中的遞歸算法。

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

從二叉樹(shù)的角度看遞歸

二叉樹(shù)天然具有遞歸的性質(zhì)。二叉樹(shù)的定義就是用二叉樹(shù)定義二叉樹(shù)。對(duì)于二叉樹(shù)的定義來(lái)說(shuō),應(yīng)該補(bǔ)充一點(diǎn):空是一棵二叉樹(shù)。

下面,我們來(lái)觀察一個(gè)二叉樹(shù)的前序遍歷的遞歸方法。

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); 這一行代碼表示“處理這個(gè)結(jié)點(diǎn)的邏輯”。

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

接下來(lái),我們將上面的代碼改造一下。

Java 代碼:

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

其實(shí)這樣的寫(xiě)法更像遞歸結(jié)構(gòu),因?yàn)閷?duì)于我們定義的每一個(gè)遞歸函數(shù)來(lái)說(shuō),應(yīng)該包含下面兩個(gè)部分:1、遞歸終止條件;2、設(shè)立遞歸過(guò)程,定義清楚函數(shù)的語(yǔ)義。

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

接下來(lái),我們?cè)倏匆粋€(gè)方法:在一個(gè)二叉樹(shù)中查看是否存在一個(gè)鍵值。寫(xiě)這個(gè)遞歸方法的步驟:想想(1)遞歸終止條件是什么?(2)遞歸過(guò)程是什么?參數(shù) Node node 是什么意思?我們這里定義的參數(shù) Node 對(duì)象,是在以 node 為根結(jié)點(diǎn)的二叉樹(shù)中尋找指定的 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)的二叉樹(shù)?

例題

例1:LeetCode 第 104 題:求一棵二叉樹(shù)的最大深度。

傳送門(mén):英文網(wǎng)址:104. Maximum Depth of Binary Tree ,中文網(wǎng)址:104. 二叉樹(shù)的最大深度 。

給定一個(gè)二叉樹(shù),找出其最大深度。

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

說(shuō)明: 葉子節(jié)點(diǎn)是指沒(méi)有子節(jié)點(diǎn)的節(jié)點(diǎn)。

示例:
給定二叉樹(shù) [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

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

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
        # 先計(jì)算左右子樹(shù),然后再計(jì)算自己,這是后序遍歷
        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

說(shuō)明:這個(gè)寫(xiě)法看一看就好,感覺(jué)沒(méi)啥意思。

感覺(jué)遞歸調(diào)用就像什么都沒(méi)有做一樣。通過(guò)這個(gè)例子,我們來(lái)理解一下(1)(2)這兩個(gè)步驟的具體應(yīng)用。這讓我想起了八皇后問(wèn)題。

還可以使用 DFS 和 BFS 完成這個(gè)問(wèn)題。首先 BFS 我覺(jué)得思路更直接一些,代碼也是有套路的。

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

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


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í)和二叉樹(shù)相關(guān)的所有操作。

練習(xí)

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

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

給定一個(gè)二叉樹(shù),找出其最小深度。

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

說(shuō)明: 葉子節(jié)點(diǎn)是指沒(méi)有子節(jié)點(diǎn)的節(jié)點(diǎn)。

示例:

給定二叉樹(shù) [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回它的最小深度 2.

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

  • 這個(gè)問(wèn)題里面有小的陷阱。

  • 我們?cè)谒伎歼f歸終止條件的時(shí)候,有的時(shí)候可能會(huì)存在陷阱。

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

這道題,我第一次做是想當(dāng)然,順著第 104 題把最大改成最小,但是要注意到上圖 4 那個(gè)結(jié)點(diǎn),4 的左孩子為空,返回 0 ,右孩子為 9,返回2,按照我們的邏輯就返回 0 ,顯然是錯(cuò)誤的,所以要針對(duì)左右孩子有一個(gè)為空的時(shí)候,做出分類(lèi)判斷。

Python 代碼:

# Definition for a binary tree node.
# 111. 二叉樹(shù)的最小深度
# 給定一個(gè)二叉樹(shù),找出其最小深度。
#
# 最小深度是從根結(jié)點(diǎn)到最近葉子結(jié)點(diǎn)的最短路徑上的結(jié)點(diǎn)數(shù)量。
#
# 說(shuō)明: 葉子結(jié)點(diǎn)是指沒(méi)有子結(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:==使用層序遍歷,我感覺(jué)更直接一些,因?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. 二叉樹(shù)的最小深度
# 給定一個(gè)二叉樹(shù),找出其最小深度。
#
# 最小深度是從根結(jié)點(diǎn)到最近葉子結(jié)點(diǎn)的最短路徑上的結(jié)點(diǎn)數(shù)量。
#
# 說(shuō)明: 葉子結(jié)點(diǎn)是指沒(méi)有子結(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)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容