題目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
參考: