一、路徑
1.二叉樹的所有路徑 leetcode 257

思路:深度遍歷
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
self.paths = []
def binaryTreePaths(self, root: TreeNode) -> List[str]:
def dfs(root,path):
if not root.left and not root.right:#葉子節點
path += str(root.val)
self.paths.append(path)
return
path += str(root.val)+'->'
if root.left:
dfs(root.left,path)
if root.right:
dfs(root.right,path)
if root == None:
return []
elif not root.left and not root.right:
return [str(root.val)]
else:
dfs(root,'')
return self.paths
2.求根到葉子節點數字之和 leetcode 129


思路:深度遍歷,遞歸保存每一條路徑
這里用int(s)就可以將以0開頭的字符串'00123...'轉成數字,或者將很多0組成的‘000...’轉成0
‘001’--> int('001')=1
'000'-->int('000')=0
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def __init__(self):
self.paths = []
def sumNumbers(self, root: TreeNode) -> int:
def dfs(root,path):
if not root.left and not root.right:#葉子
path += str(root.val)
self.paths.append(path)
return
path += str(root.val)
if root.left:
dfs(root.left,path)
if root.right:
dfs(root.right,path)
if not root:
return 0
if not root.left and not root.right:
return root.val
path = ''
dfs(root,path)
res = 0
# print(self.paths)
for i_path in self.paths:
if len(i_path) * '0' == i_path:#全0
continue
elif i_path.startswith('0'):#以0開頭
while len(i_path) > 0:
if i_path.startswith('0'):
i_path = i_path[1:]
else:
print(i_path)
res += eval(i_path)
break
else:#正常
print(eval(i_path))
res += eval(i_path)
二、路徑總和
1.路徑總和 leetcode112
給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定如下二叉樹,以及目標和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因為存在目標和為 22 的根節點到葉子節點的路徑 5->4->11->2。
思路: 用棧將遞歸轉成迭代的形式。深度優先搜索在除了最壞情況下都比廣度優先搜索更快。 最壞情況是指滿足目標和的 root->leaf 路徑是最后被考慮的,這種情況下深度優先搜索和廣度優先搜索代價是相通的。 利用深度優先策略訪問每個節點,同時更新剩余的目標和。 所以我們從包含根節點的棧開始模擬,剩余目標和為 sum - root.val。 然后開始迭代:彈出當前元素,如果當前剩余目標和為 0 並且在葉子節點上返回 True; 如果剩余和不為零並且還處在非葉子節點上,將當前節點的所有孩子以及對應的剩余和壓入棧中。
時間復雜度:和遞歸方法相同是O(N)。 空間復雜度:當樹不平衡的最壞情況下是O(N) 。在最好情況(樹是平衡的)下是 O(log N)。
# class TreeNode(object):
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def hasPathSum(self, root, sum):
"""
:type root: TreeNode
:type sum: int
:rtype: bool
"""
if not root:
return False
de = [(root, sum - root.val), ]
while de:
node, curr_sum = de.pop()
if not node.left and not node.right and curr_sum == 0:
return True
if node.right:
de.append((node.right, curr_sum - node.right.val))
if node.left:
de.append((node.left, curr_sum - node.left.val))
return False
2.路徑總和| leetcode113
給定一個二叉樹和一個目標和,找到所有從根節點到葉子節點路徑總和等於給定目標和的路徑。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定如下二叉樹,以及目標和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
]


思路:
- 從根節點深度遍歷二叉樹,先序遍歷時,將該節點值存儲值path棧中,使用path_value累加節點值;
- 當遍歷至葉節點時,檢查path_value值是否為sum,若為sum,則將path push進入result中;
- 在后續遍歷時,將該節點值從path棧中彈出,path_value減去節點值。




代碼1:用棧
class Solution:
def pathSum2(self, root, target, ):
self.result = []
path = []
def helper(root, target, path):
if root == None:
return
path.append(root.val)
if (root.left == None) and (root.right == None) and (sum(path) == target):
tmp = path[:]
self.result.append(tmp)
helper(root.left, target, path)
helper(root.right, target, path)
path.pop()
helper(root, target, path)
return self.result
代碼2:
class Solution (object):
def pathSum(self,root,sum):
"""
:type root: TreeNode
:type sum: int
:rtype: List[List[int]]
"""
res = []
if not root: return []
def helper(root, sum, tmp):
if not root:
return
if not root.left and not root.right and sum - root.val == 0:
tmp += [root.val]
res.append (tmp)
return
helper (root.left, sum - root.val, tmp + [root.val])
helper (root.right, sum - root.val, tmp + [root.val])
helper (root, sum, [])
return res
3.路徑總和|| leetcode437
給定一個二叉樹,它的每個結點都存放着一個整數值。
找出路徑和等於給定數值的路徑總數。
路徑不需要從根節點開始,也不需要在葉子節點結束,但是路徑方向必須是向下的(只能從父節點到子節點)。
二叉樹不超過1000個節點,且節點數值范圍是 [-1000000,1000000] 的整數。
示例:
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
5 -3
/ \ \
3 2 11
/ \ \
3 -2 1
返回 3。和等於 8 的路徑有:
1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11
三、最大路徑和
1.最大路徑和 leetcode124

題目:給定一棵二叉樹,求各個路徑的最大和,路徑可以以任意節點作為起點和終點。
思路:后序遍歷,每次遞歸的時候只返回左子樹/右子樹+root.data,最大值是全局記錄的,所以不需要返回
最大路徑和:根據當前節點的角色,路徑和可分為兩種情況:
一:以當前節點為根節點
1.只有當前節點
2.當前節點+左子樹🌲
3.當前節點+右子樹🌲
4.當前節點+左右子樹
這四種情況的最大值即為以當前節點為根的最大路徑和
此最大值要和已經保存的最大值比較,得到整個樹的最大路徑值
二:當前節點作為父節點的一個子節點
和父節點連接的話則需取【單端的最大值】
1.只有當前節點
2.當前節點+左子樹🌲
3.當前節點+右子樹🌲
這三種情況的最大值
class TreeNode:
def __init__(self,data):
self.data = data
self.left = None
self.right = None
class IntRef:
def __init__(self):
self.val = None
class Solution:
def findMaxPathRecursive(self,root,maxs):
if root == None:
return 0
leftmax = self.findMaxPathRecursive(root.left,maxs)
rightmax = self.findMaxPathRecursive(root.right,maxs)
tmp = max(max(leftmax+root.data,rightmax+root.data),leftmax+rightmax+root.data)
if tmp > maxs.val:
maxs.val = tmp
print(maxs)
submax = leftmax if leftmax > rightmax else rightmax
return root.data + submax
def findMaxPath(self,root):
maxs = IntRef()
maxs.val = -2 **31
self.findMaxPathRecursive(root,maxs)
return maxs.val
if __name__ == '__main__':
root = TreeNode(2)
left = TreeNode(3)
right = TreeNode(5)
root.lef= left
root.right = right
S = Solution()
maxs = S.findMaxPath(root)
print(maxs)
四、最長同值路徑
1.最長同值路徑 leetcode687
給定一個二叉樹,找到最長的路徑,這個路徑中的每個節點具有相同值。這條路徑可以經過也可以不經過根節點。
注意:兩個節點之間的路徑長度由它們之間的邊數表示。
示例1:
輸入:
5
/ \
4 5
/ \ \
1 1 5
輸出:2
示例 2:
輸入:
1
/ \
4 5
/ \ \
4 4 5
輸出:2
注意: 給定的二叉樹不超過10000個結點.樹的高度不超過1000。
思路:先解釋一下題目,就是讓我們找到一個路徑,這個路徑上面的值都相同,而且可以不經過根節點,例如,例2中的4-4-4這樣的。 可以使用遞歸來做,首先,求出以每個節點為根節點的最長路徑,然后從底向上,判斷與父親節點的值是否相同,如果相同,就把當前結點最長的一個分支路徑加上1返回給父節點。其中,可以把最長路徑保存到一個全局變量中。
class Solution (object):
def longestUnivaluePath(self, root):
"""
:type root: TreeNode
:rtype: int
"""
maxL = 0
def getMaxL(node,val):
nonlocal maxL #用來在函數或其他作用域中使用外層(非全局)變量。
if node == None:
return 0
leftMaxL = getMaxL(node.left,node.val)
rightMaxL = getMaxL(node.right,node.val)
maxL = max(maxL, leftMaxL + rightMaxL)
print('node=%s,maxL=%s'%(node.val,maxL))
if node.val == val:
return max(leftMaxL,rightMaxL) + 1
else:
return 0
if root != None:
getMaxL(root,root.val)
return maxL
def longestUnivaluePath1(self,root):
res = 0
def dns(node):
nonlocal res
if node == None:
return 0
lmax = dns(node.left)
rmax = dns(node.right)
if node.left and node.left.val == node.val:
lmax = lmax + 1
else:
lmax = 0
if node.right and node.right.val == node.val:
rmax = rmax + 1
else:
rmax = 0
res = max(res,lmax+rmax) #最大值是當前的最大值或者左右孩子路徑的和。
return max(lmax,rmax) #返回值是左右路徑中的最大值,因為它還需要和父節點繼續構建路徑。
dns(root)
return res
root = TreeNode(1)
Node1 = TreeNode(4)
Node2 = TreeNode(5)
Node3 = TreeNode(5)
Node4 = TreeNode(4)
Node5 = TreeNode(5)
Node6 = TreeNode(5)
root.left = Node1
root.right = Node2
Node1.left = Node3
Node1.right = Node4
Node2.right = Node5
Node3.left = Node5
Node5.left = Node6
S = Solution()
res = S.longestUnivaluePath1(root)
print(res)
五、公共祖先
1.找出排序二叉樹上任意兩個節點的公共父節點
思考:
- 兩個節點的公共祖先一定在從根節點,至這兩個節點的路徑上;
- 由於求公共祖先中的最近公共祖先,那么即同時出現在這兩條路徑上的離根節點最遠的節點(或離兩個最近);
- 最終算法即:求p節點路徑,q節點路徑,兩路徑上最后一個相同的節點。

思路:先序遍歷
- 從根節點遍歷(搜索)至該節點,找到該節點后就結束搜索;
- 將遍歷過程中遇到的節點按照順序存儲起來,這些節點即路徑節點。
- 求出較短路徑的長度n;
- 同時遍歷p節點的路徑和q節點的路徑,遍歷n個節點,最后一個發現的相同節點,即最近公共祖先。

class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
#獲取二叉樹從根節點root到node結點的路徑
def getPathFromRoot(root,node,s):
'''
:param root:
:param node: 二叉樹中的某個節點
:param s: 用來存儲路徑的棧
:return: node在root的子樹上
'''
if root == None:
return False
if root == node:
s.append(root)
return root
if getPathFromRoot(root.left,node,s):
s.append(root)
return True
elif getPathFromRoot(root.right,node,s):
s.append(root)
return True
return False
#查找二叉樹中兩個節點最近的公共父節點
def FindParentNode(root,node1,node2):
s1 = []
path1 = getPathFromRoot(root,node1,s1)
s2 = []
path2 = getPathFromRoot(root,node2,s2)
for ss1 in s1:
print(ss1.val,end=',')
print()
for ss2 in s2:
print(ss2.val,end=',')
#找s1,s2第一個相等的節點,就是最近的公共父節點
for i in s1:
for j in s2:
if i == j:
return i.val
L1 = TreeNode(6)
L2 = TreeNode(3)
L3 = TreeNode(-7)
L4 = TreeNode(-1)
L5 = TreeNode(9)
L1.left = L2
L1.right = L3
L2.left = L4
L2.right = L5
res = FindParentNode(L1,L3,L4)
print('公共節點',res)
