LeetCode: 深度优先遍历搜索&广度搜索专题

1091.二进制矩阵中的最短路径

题目: 一个格子,填充0或者1.只能走0的位置。从左上角到右下角最短的路径长度。如果没有这样的路径,返回-1。 可以从8个方向走。
思路:

  • 最短路径的问题,如迷宫啦,都是BFS。同时要标记走过的路径,不要重复走了;
  • 从(0,0)走,先走一步,把8个方向都做一遍,同时新的地方的值为0,则是合法的位置,入队。
  • 在入队之前,判断一下是否已经走到终点了。
from collections import deque
class Solution:
    def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
        m,n = len(grid), len(grid[0])
        if grid[0][0] != 0:
            return -1
        # 第一个位置为0, 则加入到queue
        queue = deque([[0,0]])
        dir_x = [1,1,0,-1,-1,-1,0,1]
        dir_y = [0,1,1,1,0,-1,-1,-1]
        path_len = 1
        grid[0][0] = 1 # 进栈代表走过, 走过的位置标记为1 不再走了
        if 0==m-1==n-1:
            return path_len
        while queue:
            length = len(queue)
            for i in range(length):
                x,y = queue.popleft()
                for d in range(len(dir_x)):
                    x1, y1 = x+dir_x[d], y+dir_y[d]
                    if self.valid(x1, y1, m, n):
                        if grid[x1][y1] == 0: # 可以走到这里
                            if x1 == m-1 and y1 == n-1:  # 走到终点
                                return path_len+1
                            else: # 如果不是终点
                                grid[x1][y1] = 1
                                queue.append([x1,y1])
            path_len += 1
        return -1
    def valid(self, x, y, m,n):
        if x<0 or y<0 or x>=m or y >=n:
            return False
        return True

51. N 皇后

  • N个皇后每一行每一列只能有一个。因此我们可以用rows记录每一行中哪一列能放。
  • 从第一行开始选择,如果能放,则记录下位置,继续下一行。回溯之
  • 那如何能判断能放呢?我们利用三样东西,主对角线上满足row-col == const & 副对角线上满足row+col == const & cols[col] == 0(代表这一列还没有皇后占用)
class Solution:
    def solveNQueens(self, n: int) -> List[List[str]]:
        main_diag = [0] * (2*n-1)
        deputy_diag = [0] * (2*n-1)
        queue = set()
        ans = []
        cols = [0] * n
        rows = [0] * n
        def can_place(row, col):
            return not (cols[col] + main_diag[row-col] + deputy_diag[row+col])
        def add_answer():
            temp = []
            for col in rows:
                string = '.'*col + 'Q' + '.'*(n-1-col)
                temp.append(string)
            ans.append(temp) 
        def place(row, col):
            cols[col] = 1
            rows[row] = col
            main_diag[row-col] = 1
            deputy_diag[row+col] = 1
        def cancel_place(row, col):
            cols[col] = 0
            rows[row] = 0
            main_diag[row-col] = 0
            deputy_diag[row+col] = 0
        def backtrack(row):
            if row == n:
                add_answer()
                return
            for col in range(n):
                if can_place(row, col):
                    place(row, col)
                    backtrack(row+1)
                    cancel_place(row, col)
        backtrack(0)
        return ans

329.矩阵中的最长递增路径

  • 搜索条件:不能越界;后一个位置的值要大于当前位置
  • 每个dfs(i,j) 返回以(i,j)为起点的最长递增路径
  • 暴力法超时,用cache数组记录下(i,j)的最大递增路径的值
class Solution:
    def longestIncreasingPath(self, matrix: List[List[int]]) -> int:
        dir = [[0,1],[0,-1],[1,0],[-1,0]]
        def dfs(i,j):
            nonlocal ans
            if cache[i][j] != 0:
                return cache[i][j]
            for d in dir:
                x, y = i+d[0], j+d[1]
                if x <0 or x>=m or y<0 or y>=n or matrix[i][j] >= matrix[x][y]:
                    continue
                cache[i][j] = max(cache[i][j], dfs(x,y))
            cache[i][j] += 1
            ans = max(ans, cache[i][j])
            return cache[i][j]
        if not matrix or not matrix[0]:
            return 0
        m,n = len(matrix), len(matrix[0])

        ans = 0
        cache =[[0]*n for _ in range(m)]
        for i in range(m):
            for j in range(n):
                dfs(i,j)
        return ans

394.字符串解码(深度优先)

  • 遇到左括号,目前已经解码的部分和要重复的数字进栈保存。同时代表解码的部分的遍历初始化为‘’。
  • 如果遇到右括号,出栈, 重新解码的ans代表的就是应该重复的部分。
    参考leetcode
class Solution:
    def decodeString(self, s: str) -> str:
        stack = []
        n = len(s)
        ans = ''
        temp = ''
        num = 0
        for i in range(n):
            if s[i].isdigit():
                num = num*10 + int(s[i])
            elif s[i] == '[':
                stack.append((ans, num))
                num = 0
                ans = ''
            elif s[i] == ']':
                top = stack.pop()
                ans = top[0] + ans * top[1]
            else:
                ans += s[i]
        return ans 

单词拆分||

单词拆分|是求字典里面的单词能否组成一个string。||的要求是,把所有的组合罗列出来。动态规划和回溯递归都能做。但暴力法的递归是超时的。动态规划的dp[i]记录以i为结尾的串的所有合法组合,所以dp[i]是个list。
我写的是记忆化回溯

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> List[str]:
        n = len(s)
        rec = {}
        def dfs(index):
            if index in rec:
                return rec[index]
            res =[]
            if index == n:
                return [""]
            for i in range(index, n):
                sub = s[index:i+1]
                if sub in wordDict:
                    temp = dfs(i+1)
                    for a in temp:
                        res.append(sub+' '+a if len(a)!=0 else sub)
            rec[index] = res
            return res  
        ans = dfs(0)
        return ans

这道题的小坑是,不能用基本模板来写。我们想记录index到最后位置的子串能被字典单词组合的所有组合。这个是无法通过在结束条件那里保存的。我们需要逐个返回以i为起点的子串的所有合法组合。

你可能感兴趣的:(LeetCode)