[LeetCode] #113. Path Sum II

題目

Given the root of a binary tree and an integer targetSum, return all root-to-leaf paths where each path's sum equals targetSum.

A leaf is a node with no children.

直覺解

構想:

把所有 root-to-leaf paths 找出來(利用 recursion + DFS)後,一一檢查總和是否為 targetSum

python 3 實作:

from copy import deepcopy 


class Solution:
    def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
        paths = []
        if not root:
            return paths

        paths.append([])
        paths = self.recursion(root, deepcopy(paths))

        ans = []
        for path in paths:
            if sum(path) == targetSum:
                ans.append(path)
        return ans

    def recursion(self, root, paths):
        if not root:
            return []

        for path in paths:
            path.append(root.val)

        if not root.left and not root.right:
            return paths

        paths_left = self.recursion(root.left, deepcopy(paths))
        paths_right = self.recursion(root.right, deepcopy(paths))

        paths = paths_left + paths_right
        return paths

效能:

Runtime: 572 ms, faster than 6.28% of Python3 online submissions for Path Sum II.
Memory Usage: 19.5 MB, less than 11.61% of Python3 online submissions for Path Sum II.

優化構想:

邊找 root-to-leaf paths,邊檢查檢查總和是否為 targetSum。因為節點的值可能是正的或負的,所以不能在中途發現 sum > targetSum 時就 return []。

python 3 實作:

from copy import deepcopy 


class Solution:
    def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
        paths = []
        if not root:
            return paths

        paths.append([])
        return self.recursion(root, deepcopy(paths), targetSum, 0)

    def recursion(self, root, paths, targetSum, sum):
        if not root:
            return []

        for path in paths:
            path.append(root.val)

        sum += root.val
        if not root.left and not root.right:
            if sum == targetSum:
                return paths
            else:
                return []

        paths_left = self.recursion(root.left, deepcopy(paths), targetSum, sum)
        paths_right = self.recursion(root.right, deepcopy(paths), targetSum, sum)

        paths = paths_left + paths_right
        return paths

效能:

Runtime: 552 ms, faster than 5.98% of Python3 online submissions for Path Sum II.
Memory Usage: 19.5 MB, less than 11.37% of Python3 online submissions for Path Sum II.

參考別人的解

來源:Python DFS

參考後的優化構想

不要使用 deepcopy。把 list 的深度降低,這樣 shallow copy 就夠用了。

自己的 python 3 實作:

class Solution: 
    def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
        paths = []
        if not root:
            return paths

        self.recursion(root, [], paths, targetSum, 0)
        return paths

    def recursion(self, root, path, paths, targetSum, sum):
        if not root:
            return

        path.append(root.val)
        sum += root.val
        if not root.left and not root.right and sum == targetSum:
            paths.append(path)
            return

        if root.left:
            self.recursion(root.left, path.copy(), paths, targetSum, sum)
        if root.right:
            self.recursion(root.right, path.copy(), paths, targetSum, sum)

        return

效能:

Runtime: 48 ms, faster than 53.55% of Python3 online submissions for Path Sum II.
Memory Usage: 19.5 MB, less than 11.37% of Python3 online submissions for Path Sum II.

研究別人的解

來源:sample 40 ms submission

研究重點:為什麼他在呼叫 pathSumRecursive 時不需要 copy currentPath

別人的 python 3 實作(已加上研究用的 print):

class Solution: 
    def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
        allPaths = []
        currentPath = []
        self.pathSumRecursive(root, targetSum, currentPath, allPaths) #forgetting something?

        return allPaths

    def pathSumRecursive(self, root, targetSum, currentPath, allPaths):
        print('<--')
        if root is None:
            print('-->')
            return
        print('root:', root.val)

        currentPath.append(root.val)
        print(currentPath)
        if root.left == None and root.right == None and targetSum == root.val:
            allPaths.append(list(currentPath)) #2

        else:
            self.pathSumRecursive(root.left, targetSum - root.val, currentPath, allPaths)
            self.pathSumRecursive(root.right, targetSum - root.val, currentPath, allPaths)

        del currentPath[-1]
        print('del:', currentPath)
        print('-->')

效能(沒有加 print 的版本):

Runtime: 36 ms, faster than 97.35% of Python3 online submissions for Path Sum II.
Memory Usage: 15.4 MB, less than 88.26% of Python3 online submissions for Path Sum II.

print 出來的東西:

Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
Output: [[5,4,11,2],[5,8,4,5]]


<-- 
root: 5
[5]
<--
root: 4
[5, 4]
<--
root: 11
[5, 4, 11]
<--
root: 7
[5, 4, 11, 7]
<--
-->
<--
-->
del: [5, 4, 11]
-->
<--
root: 2
[5, 4, 11, 2]
del: [5, 4, 11]
-->
del: [5, 4]
-->
<--
-->
del: [5]
-->
<--
root: 8
[5, 8]
<--
root: 13
[5, 8, 13]
<--
-->
<--
-->
del: [5, 8]
-->
<--
root: 4
[5, 8, 4]
<--
root: 5
[5, 8, 4, 5]
del: [5, 8, 4]
-->
<--
root: 1
[5, 8, 4, 1]
<--
-->
<--
-->
del: [5, 8, 4]
-->
del: [5, 8]
-->
del: [5]
-->
del: []
-->

研究感想

把節點 append 進去 currentPath,跑完左右兩個分枝後,用 del currentPath[-1] 把節點刪掉,重複使用同一個 currentPath,就不需要每次都複製一個新的 currentPath 來紀錄 path 了

在 pathSumRecursive 函式中,因為一直把 targetSum 減掉 root.val,所以參數位置只要放一個 targetSum 就好;不需要像我一樣,在 recursion 函式裡放了 targetSum 和 sum 兩個參數。

參考資料

研究後優化我的程式碼

python 3 實作:

class Solution: 
    def pathSum(self, root: TreeNode, targetSum: int) -> List[List[int]]:
        paths = []
        if not root:
            return paths

        self.recursion(root, [], paths, targetSum)
        return paths

    def recursion(self, root, path, paths, targetSum):
        path.append(root.val)
        targetSum -= root.val
        if not root.left and not root.right and targetSum == 0:
            paths.append(list(path))
            del path[-1]
            return

        if root.left:
            self.recursion(root.left, path, paths, targetSum)
        if root.right:
            self.recursion(root.right, path, paths, targetSum)
        del path[-1]

        return

效能:

Runtime: 36 ms, faster than 97.35% of Python3 online submissions for Path Sum II.
Memory Usage: 15.1 MB, less than 95.32% of Python3 online submissions for Path Sum II.

Comments

Popular posts from this blog

資料關聯

程式設計相關社群、活動

TCP/ IP 通訊協定