6.2 數據結構---樹(路徑)


一、路徑

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]
]

思路:

  1. 從根節點深度遍歷二叉樹,先序遍歷時,將該節點值存儲值path棧中,使用path_value累加節點值;
  2. 當遍歷至葉節點時,檢查path_value值是否為sum,若為sum,則將path push進入result中;
  3. 在后續遍歷時,將該節點值從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.找出排序二叉樹上任意兩個節點的公共父節點

思考

  1. 兩個節點的公共祖先一定在從根節點,至這兩個節點的路徑上;
  2. 由於求公共祖先中的最近公共祖先,那么即同時出現在這兩條路徑上的離根節點最遠的節點(或離兩個最近);
  3. 最終算法即:求p節點路徑,q節點路徑,兩路徑上最后一個相同的節點。

思路:先序遍歷

  1. 從根節點遍歷(搜索)至該節點,找到該節點后就結束搜索;
  2. 將遍歷過程中遇到的節點按照順序存儲起來,這些節點即路徑節點。
  3. 求出較短路徑的長度n;
  4. 同時遍歷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)

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM