DFS和回溯。LeetCode200岛屿数目;LeetCode46全排列;LeetCode17电话号码的字母组合;牛客HJ43迷宫问题

我个人感觉回溯就是DFS的一种应用。
DFS有4个比较重要的点,或者说4个步骤

1. 一个是在main函数中找到进行DFS的条件
2. 另一个就是DFS函数中停止DFS,进行return的条件,
3. DFS需要进行的方向,也就是DFS中的参数设计
4. 注意搜索过的路径或者值的状态,进行记录。

而回溯更强调搜过之后恢复状态,个人认为,很多排列组合的场景都可以用回溯的思想搜索出来。

leetcode 200 岛屿数目

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
样例1 
输入:grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
输出:1

样例2
输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3
直接来看,
首先是main函数中进行DFS的条件,我们要进行DFS的条件肯定是碰到了‘1’,那么就继续DFS,
而DFS中需要停止的条件就是如果碰到了‘0’,就return
再看DFS进行的方向,这个就根据题目中定义来看,我们要找岛屿,肯定要找一个1的上下左右
dfs(grid, i, j-1), dfs(grid, i, j+1), dfs(grid, i-1, j), dfs(grid, i+1, j)
然后对于遍历过的1,我们直接在原数组上赋值一个非01的值就行。

审视一般,OK没有?
仔细看看会发现,如果这么进行DFS的话,那过了边界怎么办?比如小于0或者大于m或者n?所以这个也是return的条件的。

下面是代码
class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        # 我们需要以不同的点为起点进行dfs搜索,每当dfs搜索结束的时候,岛屿数目+1
        # 对于搜索过的点,需要做标记,我们记为2
        # 搜索的方式和迷宫一样,上下左右即可,对于搜索结束的标志要注意,碰到0,2或者边界都要结束
        def dfs(grid, i, j, m, n):           
            if i<0 or j<0 or i>m-1 or j>n-1:
                return 
            if grid[i][j] == '0' or grid[i][j]=='2':
                return 
            grid[i][j]='2' 

            dfs(grid, i-1, j, m, n)
            dfs(grid, i, j-1, m, n)
            dfs(grid, i+1, j, m, n)
            dfs(grid, i, j+1, m, n)


        count = 0
        m,n = len(grid), len(grid[0])
        for i in range(m):
            for j in range(n):
                if grid[i][j]=='1':
                    dfs(grid, i, j, m, n)
                    count+=1
        return count


牛客网华为机考 HJ43 迷宫唯一路径问题

**输入描述:**
输入两个整数,分别表示二维数组的行数,列数。再输入相应的数组,其中的1表示墙壁,0表示可以走的路。数据保证有唯一解,不考虑有多解的情况,即迷宫只有一条通道。
**输出描述**:
左上角到右下角的最短路径,格式如样例所示。
输入
5 5
0 1 0 0 0
0 1 1 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
输出
(0,0)
(1,0)
(2,0)
(2,1)
(2,2)
(2,3)
(2,4)
(3,4)
(4,4)
解题############################思路# print(m,n, arr)
通过标记的方法让dfs不走回头路,避免死循环,标记点添加到path中
若通过arr[i][j]无法达到终点,那么path要pop出来这个点,同时复原标记
解答如下
def dfs(arr, i, j, path):
    if i==len(arr)-1 and j == len(arr[0])-1:
        path.append((i,j)) # 到达终点
        for z in range(len(path)):
            print('('+str(path[z][0])+','+str(path[z][1])+')' )
        return
    if not 0<=i<len(arr) or not 0<=j<len(arr[0]) or (i,j) in path or arr[i][j]==1: #开始新的dfs
        return
    
    path.append((i,j))

    dfs(arr, i-1, j, path)
    dfs(arr, i+1, j, path)
    dfs(arr, i, j-1, path)
    dfs(arr, i, j+1, path)

    path.pop() #此路不通,退回到上一个节点,岛屿不用退是因为岛屿有mxn个起点,而迷宫只有一条路

while True:
    try:
        n,m = list(map(int,input().split(' ')))
        arr = [list(map(int, input().split(' '))) for _ in range(n)]
        path = []
        dfs(arr, 0,0,path)
    except:
        break

LeetCode 46全排列

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

输入:nums = [0,1]
输出:[[0,1],[1,0]]

输入:nums = [1]
输出:[[1]]
对于排列组合这种问题,一般首先考虑回溯,因为都是用已有组合和剩下组合进行匹配的过程。
这个过程就是 **搜索 添加结果 撤回 继续搜索**。
下面是我第一个解答,首先我没有在dfs之外使用for循环,因为这样不方便处理for循环中剩下的index,
然后就是从return那里开始倒推,但是我这里解法明显有问题,
输入[1,2,3] 
正确结果应该是[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
而我这个只输出了[1,2,3]
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        def dfs(res, path ,nums, i):
            if i == len(nums):
                res.append(path)
                return 
            else:
                path.append(nums[i])
                print(path)
                dfs(res, path, nums, i+1)
        res = []
        path = []
        dfs(res, path, nums, 0)
        return res
        
上面代码的逻辑没什么问题,但是细节处理不好,
首先它这里只以1为起点,不能以2或者3为起点,,这需要我们加入1之后再撤回1
其次它不能1 12 123 而是要[1] [12,13] [123,132]这需要我们加入2之后再撤回2,加入3
所以我考虑用一个visited的数组记录数组nums中元素有没有被访问过。从这个节点i添加到path,那么这个点的visited[i]就是True,而以i为起点的dfs结束之后,我们需要把visited[i]重置为False,同时还要从path中把它pop出来,便于以后面的元素为起点的时候可以重复用到它。
另外注意有pop操作,所以res添加path的时候需要添加每一次的copy,因为path在pop之后会改变其内容。
class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        def dfs(res, path ,nums, depth, visited):
            if depth == len(nums):
                res.append(path.copy())
                return 
            for j,v in enumerate(visited):
                if visited[j]==False:
                    path.append(nums[j])
                    visited[j]=True      
                    dfs(res, path, nums, depth+1, visited)
                    visited[j]=False
                    path.pop()
                
        res = []
        path = []
        visited = [False]*len(nums)
     
        dfs(res, path, nums, 0, visited)       
        return res

        
            
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值