Stack and DFS

本文蒐集

LeetCode 200: Number of Islands

Given an m x n 2D binary grid grid which represents a map of '1's (land) and '0's (water), return the number of islands.

An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Example 1:

Input: grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
Output: 1
Example 2:
Input: grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
Output: 3
Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] is '0' or '1'.

我的解法

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:            
        queue = []
        count = 0
        
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j] == '1':
                    self.dfs(grid, i, j)
                    count += 1
        return count
    
    def dfs(self, grid, i, j):
        
        if i < 0 or j < 0 or i >= len(grid) or j >= len(grid[0]) or grid[i][j] != '1':
            return
        
        grid[i][j] = '0'
        self.dfs(grid, i+1, j)
        self.dfs(grid, i-1, j)
        self.dfs(grid, i, j+1)
        self.dfs(grid, i, j-1)

LeetCode 133: Clone Graph

Given a reference of a node in a connected undirected graph.

Return a deep copy (clone) of the graph.

Each node in the graph contains a val (int) and a list (List[Node]) of its neighbors.

class Node {
    public int val;
    public List<Node> neighbors;
}
Test case format:

For simplicity sake, each node's value is the same as the node's index (1-indexed). For example, the first node with val = 1, the second node with val = 2, and so on. The graph is represented in the test case using an adjacency list.

Adjacency list is a collection of unordered lists used to represent a finite graph. Each list describes the set of neighbors of a node in the graph.

The given node will always be the first node with val = 1. You must return the copy of the given node as a reference to the cloned graph.

Example:

Constraints:

  • 1 <= Node.val <= 100
  • Node.val is unique for each node.
  • Number of Nodes will not exceed 100.
  • There is no repeated edges and no self-loops in the graph.
  • The Graph is connected and all nodes can be visited starting from the given node.

我的解法

# Definition for a Node.
class Node:
    def __init__(self, val = 0, neighbors = None):
        self.val = val
        self.neighbors = neighbors if neighbors is not None else []
"""

class Solution:
    def cloneGraph(self, node: 'Node') -> 'Node':
        if node is None:
            return
        
        self.nodes = [None] * 100
        self.dfs(node)
        for i, _ in enumerate(self.nodes):
            if _ is not None:
                return _
        
    def dfs(self, node: 'Node') -> 'Node':
        if self.nodes[node.val-1] is None:
            n = Node(val = node.val)
            self.nodes[node.val-1] = n
             
            for i in node.neighbors:
                self.dfs(i)
                n.neighbors.append(self.nodes[i.val-1])
            
        else:
            return

LeetCode 494: Target Sum

You are given an integer array nums and an integer target.

You want to build an expression out of nums by adding one of the symbols '+' and '-' before each integer in nums and then concatenate all the integers.

For example, if nums = [2, 1], you can add a '+' before 2 and a '-' before 1 and concatenate them to build the expression "+2-1". Return the number of different expressions that you can build, which evaluates to target.

Example 1:

Input: nums = [1,1,1,1,1], target = 3
Output: 5
Explanation: There are 5 ways to assign symbols to make the sum of nums be target 3.
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
Example 2:
Input: nums = [1], target = 1
Output: 1

Constraints:

  • 1 <= nums.length <= 20
  • 0 <= nums[i] <= 1000
  • 0 <= sum(nums[i]) <= 1000
  • -1000 <= target <= 1000

我的解法 1

class Solution:
    def findTargetSumWays(self, nums: List[int], S: int) -> int:
        if nums is None:
            return
        if len(nums) == 1:
            if nums[0] == 0 and S == 0:
                return 2
            elif nums[0] == abs(S):
                return 1
            else:
                return 0
        else:
            n1 = self.findTargetSumWays(nums[:-1], S + nums[-1])
            n2 = self.findTargetSumWays(nums[:-1], S - nums[-1])
            return n1 + n2

我的解法 2

利用 dict 字典紀錄搭配 list 兩層紀錄已計算過的資訊,但效率不好

class Solution:
    def findTargetSumWays(self, nums: List[int], S: int) -> int:
        self.arrived = [dict()]*len(nums)
        print(self.arrived)
        return self.dfs(nums, S)
    def dfs(self, nums: List[int], S: int) -> int:
        if len(nums) == 1:
            if nums[0] == abs(S):
                if S == 0:
                    self.arrived[0][S] = 2
                    return 2
                else:
                    self.arrived[0][S] = 1
                    return 1
            else:
                self.arrived[0][S] = 0
                return 0
        else:
            n1 = self.arrived[len(nums)-2].get(S-nums[-1], self.dfs(nums[:-1], S-nums[-1]))
            n2 = self.arrived[len(nums)-2].get(S+nums[-1], self.dfs(nums[:-1], S+nums[-1]))
            self.arrived[len(nums)-1][S] = n1 + n2
            return n1 + n2

我的解法 3

利用 @functools.cache 快取

可參考 docs

class Solution:
    def findTargetSumWays(self, nums: List[int], S: int) -> int:
        @functools.cache
        def _findWays(idx, remaining):
            if idx == len(nums):
                return remaining == 0
            return _findWays(idx+1, remaining-nums[idx]) + _findWays(idx+1, remaining+nums[idx])
        return _findWays(0, S)

我的解法 4

利用 dict 字典和 set 集合紀錄已計算過的資訊效率不錯

class Solution: 
    def findTargetSumWays(self, nums: List[int], S: int) -> int:
        dp = {}
        
        def dfs(i=0, total=0):
            
            if i >= len(nums):
                return 1 if total == S else 0
            
            key = (i, total)
            if key not in dp:
                dp[key] = dfs(i+1, total+nums[i]) + dfs(i+1, total-nums[i])
            
            return dp[key]
        
        return dfs()

LeetCode 94: Binary Tree Inorder Traversal

Given the root of a binary tree, return the inorder traversal of its nodes' values.

Example:

Constraints:

  • The number of nodes in the tree is in the range [0, 100].
  • -100 <= Node.val <= 100

Follow up:

Recursive solution is trivial, could you do it iteratively?

我的解法 1

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

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        result = []
        self.dfs(root, result)
        return result
    
    def dfs(self, root, r):
        if root is None:
            return
        
        self.dfs(root.left, r)
        r.append(root.val)
        self.dfs(root.right, r)

我的解法 2

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

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        
        if root is None:
            return
        
        result = []
        stack = []
        visited = set()
        
        stack.append(root)
        
        while stack:
            curr = stack.pop()
            
            if curr in visited:
                result.append(curr.val)
            else:
                if curr.right:
                    stack.append(curr.right)
                stack.append(curr)
                visited.add(curr)
                if curr.left:
                    stack.append(curr.left)
        return result