【leetcode】樹and遞歸

題目1

【簡單】路徑總和

給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。

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

示例:
給定如下二叉樹,以及目標和 sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1

返回 true, 因爲存在目標和爲 22 的根節點到葉子節點的路徑 5->4->11->2

 

解答

有兩種思想:遞歸與循環.
遞歸:參照遞歸的4個步驟:1.確定函數功能;2.遞推公式,重要,只要把想出來了,就成功了一半;3.終止條件,通過舉例子方式歸納得到.4.時空複雜度. 詳見代碼註釋

循環:方法一:可以通過兩個list交替循環,這個比較通用,比如計算樹深度,打印樹等任務.
方法二:通過一個stack的pop操作來實現循環.

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

class Solution:
    # 函數功能:輸入一個root/sum,判斷是否存在
    # 遞推公式:hasPathSum(root,sum) = hasPathSum(root.left,sum-root.val) or hasPathSum(root.left,sum-root.val)
    # 終止條件:if not root: return False;if 是葉節點 and 值等於sum,return True
    # 複雜度: O(N);O(logN)
    # 只遍歷左子樹,右子樹
    def hasPathSum(self, root: TreeNode, sum: int) -> bool:
        if not root:
            return False
        if not root.left and not root.right and root.val == sum:
            return True
        left = self.hasPathSum(root.left,sum-root.val)
        right = self.hasPathSum(root.right,sum-root.val)
        return left or right

class Solution:
    # 循環.通過兩個list進行交替
    # def hasPathSum(self,root,sum):
    #     if not root:
    #         return False
    #     curLayerNode = [(root,sum)]
    #     while curLayerNode:
    #         nextLayerNode = []
    #         for (node,val) in curLayerNode:
    #             if not node.left and not node.right and node.val == val:
    #                 return True
    #             if node.left:
    #                 nextLayerNode.append((node.left,val-(node.val)))
    #             if node.right:
    #                 nextLayerNode.append((node.right,val-(node.val)))
    #         curLayerNode = nextLayerNode
    #     return False

    #循環.通過一個stack的pop進行交替
    def hasPathSum(self,root,sum):
        if not root:
            return False
        stack = [(root,sum)]
        while stack:
            node,val = stack.pop()
            if not node.left and not node.right and node.val == val:
                return True
            if node.right:
                stack.append((node.right,val-node.val))
            if node.left:
                stack.append((node.left,val-node.val))
        return False



鏈接:https://leetcode-cn.com/problems/path-sum/solution/shu-de-di-gui-yu-xun-huan-de-an-li-zhi-yi-by-zhang/

 

題目2

【簡單】二叉樹的最小深度

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

最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。

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

示例:

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

    3
   / \
  9  20
    /  \
   15   7

返回它的最小深度  2.

 

解答

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

# 遞歸解法
class Solution:
# 遞推公式:minDepth(root) = min(self.minDepth(root的子節點), min_depth)
# 終止條件:if not root: return 0;if 沒有子節點,return 1
    def minDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root:
            return 0

        children = [root.left, root.right]
        # if we're at leaf node
        if not any(children):
            return 1

        min_depth = float('inf')
        for c in children:
            if c:
                min_depth = min(self.minDepth(c), min_depth)
        return min_depth + 1


class Solution:
    def minDepth(self, root: TreeNode) -> int:
        # 利用棧,記錄深度
        if not root:
            return 0
        min_depth = 1
        stack = [(root,min_depth)]
        while stack:
            node,val = stack.pop()
            if not node.left and not node.right:
                if min_depth == 1:
                    min_depth = val
                else:
                    min_depth = min(min_depth,val)
            if node.right:
                stack.append((node.right,val+1))
            if node.left:
                stack.append((node.left,val+1))
        return min_depth

 

題目3

【簡單】最長同值路徑

給定一個二叉樹,找到最長的路徑,這個路徑中的每個節點具有相同值。 這條路徑可以經過也可以不經過根節點。

注意:兩個節點之間的路徑長度由它們之間的邊數表示。

示例 1:

輸入:

              5
             / \
            4   5
           / \   \
          1   1   5

輸出:

2

示例 2:

輸入:

              1
             / \
            4   5
           / \   \
          4   4   5

輸出:

2

解答

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

class Solution:
    def longestUnivaluePath(self, root: TreeNode) -> int:
        self.ans = 0

        def arrow_length(node):
            if not node: return 0
            left_length = arrow_length(node.left)
            right_length = arrow_length(node.right)
            left_arrow = right_arrow = 0
            if node.left and node.left.val == node.val:
                left_arrow = left_length + 1
            if node.right and node.right.val == node.val:
                right_arrow = right_length + 1
            self.ans = max(self.ans, left_arrow + right_arrow)
            return max(left_arrow, right_arrow)

        arrow_length(root)
        return self.ans

 

【補充】

算法題之遞歸調用

遞歸求解算法題有三個要素:(這裏以n的階乘爲例)

1、函數功能

// 算 n 的階乘(假設n不爲0)
int f(int n){
 /**/
}

2、終止條件

所謂遞歸,就是會在函數內部代碼中,調用這個函數本身,所以必須要找出遞歸的結束條件,不然的話,會一直調用自己,進入無限循環。也就是說,需要找出當參數爲啥時,遞歸結束,之後把結果返回。

// 算 n 的階乘(假設n不爲0)
int f(int n){
    if(n <= 2){
        return n;
    }
}

3、遞歸公式

不斷縮小參數的範圍,例如,f(n) 這個範圍比較大,我們可以讓 f(n) = n * f(n-1)

// 算 n 的階乘(假設n不爲0)
int f(int n){
    if(n <= 2){
       return n;
    }
    // 把 f(n) 的等價操作寫進去
    return f(n-1) * n;
}

 

例: 斐波那契數

斐波那契數,通常用 F(n) 表示,形成的序列稱爲斐波那契數列。該數列由 0 和 1 開始,後面的每一項數字都是前面兩項數字的和。也就是:

終止條件:

F(0) = 0,   F(1) = 1

遞歸公式:
F(N) = F(N - 1) + F(N - 2), 其中 N > 1.

給定 N,計算 F(N)。

class Solution:
    def fib(self, N: int) -> int:
        if N == 0:
            return 0
        elif N == 1:
            return 1
        else:
            return self.fib(N-1) + self.fib(N-2)

 

例:爬樓梯

假設你正在爬樓梯。需要 n 階你才能到達樓頂。

每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?

注意:給定 n 是一個正整數。

終止條件:

F(1) = 1, F(2)=2

遞歸公式:

第一種跳法:第一次跳了1個臺階,那麼還剩下n-1個臺階還沒跳,剩下的n-1個臺階的跳法有F(N-1)種。

第二種跳法:第一次跳了2個臺階,那麼還剩下n-2個臺階還沒,剩下的n-2個臺階的跳法有F(N-2)種。

F(N) =F(N-1)+F(N-2)

class Solution:
    def climbStairs(self, n: int) -> int:
        if n == 1:
            return 1
        elif n == 2:
            return 2
        else:
            return self.climbStairs(n-1) + self.climbStairs(n-2)

 

例:反轉鏈表

定義一個函數,輸入一個鏈表的頭節點,反轉該鏈表並輸出反轉後鏈表的頭節點。

示例:

輸入: 1->2->3->4->5->NULL
輸出: 5->4->3->2->1->NULL

終止條件:

當鏈表只有一個節點,或者如果是空表的話,你應該知道結果吧?直接啥也不用幹,直接把 head 返回

遞歸公式:

以下面head->1->2->3->4爲例

已經定義了 reverseLis t函數的功能可以把一個單鏈表反轉,所以,我們對 2->3->4反轉之後的結果應該是這樣:

把 2->3->4 遞歸成 4->3->2。但1 這個節點我們並沒有去碰它,所以 1 的 next 節點仍然是連接這 2。

接下來只需要把節點 2 的 next 指向 1,然後把 1 的 next 指向 null,即通過改變 newList 鏈表之後的結果如下:

也就是說,reverseList(head) 等價於  reverseList(head.next) + 改變一下1,2兩個節點的指向

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        # 1.遞歸結束條件
        if head is None or head.next is None:
              return head
        # 遞歸反轉 子鏈表
        newList = self.reverseList(head.next)
        # 改變 1,2節點的指向。
        # 通過 head.next獲取節點2
        t1  = head.next
        # 讓 2 的 next 指向 2
        t1.next = head
        # 1 的 next 指向 null
        head.next = None
        # 把調整之後的鏈表返回。
        return newList

 


 

參考:

算法練習題之遞歸調用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章