DFS实战

作者: 小碧小琳 | 来源:发表于2018-09-03 18:41 被阅读0次

若是从一二叉树树考虑,DFS就是先一路搜索到最左侧,然后逐渐返回上一节点,再搜索上一节点的子节点。有栈的思想。所以一般都会用递归的方式解决问题。

不同于BFS每次只找一层 ,先进先出的对列。而是对当前点cur进行递归,如果cur的临界点有效,就继续深搜递归,一直搜索到底为止。

一、DFS的一般步骤:

1、定义一个递归函数,在递归函数中,判断该点是否有效,(有时若是判定有效比较复杂,就单独写出一个isvalid函数判定该点是否有效。)如果有效,就对该点的相邻节点继续进行递归,一直进行到底为止。

2、对图中需要用递归函数的点,进行递归调用就行。

二、目前遇到的DFS的两种问题

2.1、有效路线问题

对于一些路线问题,相对比较难,要用栈记住路线,在搜索完一个点以后,需要逐级的弹出该点。所以在递归完以后,一定要pop出该点。

2.2.1、骑士周游问题

理解动态规划、BFS和DFS中的骑士周游问题:

nodeSet = set()
def knightTour(n,path,u,limit):
    nodeSet.add(u)
    path.append(u)
    if n < limit:
        nbrList = list(u.getConnections())
        i = 0
        done  = False
        while i < len(nbrList) and not done:
            #先判断这个点是不是已经存在于nodeSet中
            if nbrList[i] not in nodeSet:
                #不在,则对这个点继续进行递归调用函数
                done = knightTour(n+1,path,nbrList[i],limit)
            i += 1
        #如果遍历完这个节点,发现还是没有正确的路径,那么就把这个点从path中弹出
        #并把点从nodeSet中弹出,对当前点的父节点的其余的子节点进行递归调用
        if not done:
            path.pop()
            nodeSet.remove(u)
    else:
        done = True
    return done

在搜索完该点的邻接点以后,需要返回上一节点去找上一节点的邻接节点,因此pop出当前节点,把上一节点留在栈顶,在递归的时候再弹出进行搜索。

2.2.2、经典的八皇后问题

[LeetCode] N-Queens N皇后问题

解决这个问题,有四点需要注意:

  • 1、如何表示皇后的位置。
    这里参考中,是用一个长度为n初始化元素全为-1的列表pos来表示的。其中pos[i]表示第i行皇后的列数col。
    比如搜索两次以后,pos=[3,6,-1,-1,-1,-1,-1,-1]就表示此时第1行皇后位置在第4列,第二行皇后位置在第7列。

  • 2、如何找到新皇后的有效位置。
    继续上面pos=[3,6,-1,-1,-1,-1,-1,-1],在搜索到第三行时,此时就会对每一列col进行判定,如果此时col不与前面两行的皇后列数重复,并且不在一条斜线上,就是有效的。比如pos[2] = 2就是有效的,这时再对第四行进行递归调用。

  • 3、搜索完一个点以后,如何返回上一个点
    根据前面的pos的定义,在搜索到第3行第3个点时,就进行赋值pos[2]=2,此时pos=[3,6,2,-1,-1,-1,-1,-1],然后对第四行调用递归函数。
    当递归函数调用结束时,说明第3行第3个点已经搜索完了,此时应该搜索第3行的其余点,判断是否有效并且赋值。那么此时就应该令pos[2]=-1。

具体代码如下

               if isvalid(pos,row,col):
                    #若有效,就把这一行皇后位置赋值为col
                    pos[row] = col
                    dfs(pos,row+1,res)
                    pos[row] = -1
  • 4、输出结果如何表示

在搜索到底以后,就要按照题意输出一个有效的结果。然后把这个有效的结果,append到res列表中。在函数最后return res。

代码实现:

class Solution:
    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        #定义输出结果列表
        res = []

        #定义一个判断皇后位置是否有效的函数,这个在递归函数中会用到
        def isvalid(pos,row,col):
            #若新皇后的列数col与前面任何一行的一个皇后的列数重复,或者斜着,都是无效的
            for i  in range(row):
                if col == pos[i] or abs(row - i) == abs(col - pos[i]):
                    return False
            return True

        #先定义pos,长度为n,pos[i]代表第i行中皇后所处的列,其中每个元素初始化为-1
        pos = []
        for i in range(n):
            pos.append(-1)

        #定义递归函数,传入参数pos,n,res,返回一个有效结果out
        #传入pos列表用来追踪已有的每行皇后的位置,传入n用来判断是否结束递归,
        # 传入res用来存储有效结果out
        def dfs(pos,row,res):
            if row == n:
                out = []
                for i in range(n):
                    out.append(pos[i] * '.' + 'Q' + (n-1-pos[i]) * '.')
                res.append(out)
                return res
            else:
                for col in range(n):
                    if isvalid(pos,row,col):
                        #若有效,就把这一行皇后位置赋值为col
                        pos[row] = col
                        dfs(pos,row+1,res)
                        #对于row,col位置的搜索全部结束以后,就舍弃掉col,去找col+1是否有效,
                        #是否需要继续递归
                        pos[row] = -1

        #调用递归函数,传入row为0
        dfs(pos,0,res)
        return res

2.2、二维数组中的一些问题

比如numIslands这道题,对于一些1相邻的点,递归搜索到底并且把1变成0以后,不需要逐级在返回时,把访问完的点再次进行弹出。那么就不用考虑这个pop的问题。

求节目标是有几个解时,用DFS。
题目见200. Number of Islands

代码实现:

class Solution:
    def numIslands(self, grid):
        if len(grid) == 0:
            return 0
        # 获取行列数
        m = len(grid)
        n = len(grid[0])


        # 定义一个递归函数,传入一个有效点,visitred,
        # 不返回值,但会把相邻的有效点坐标都添加进visited中去
        def recur(location):
            i = location[0]
            j = location[1]
            if i >= 0 and i <= m - 1 and j >= 0 and j <= n - 1 and grid[i][j] == '1':
                grid[i][j] = '0'
                for loc in [(i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)]:
                    recur(loc)

        # 主函数,如果发现以后新的1,并且1的坐标不在visited中,num加1,并且对该点调用递归函数
        num = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j] == '1' :
                    num += 1
                    recur((i, j))
        return num

相关文章

  • DFS实战

    若是从一二叉树树考虑,DFS就是先一路搜索到最左侧,然后逐渐返回上一节点,再搜索上一节点的子节点。有栈的思想。所以...

  • 各种DFS

    DFS邻接矩阵遍历图 DFS邻接表遍历图 DFS回溯(不走重复路径) DFS背包(可重复选) DFS背包(不可重复选)

  • HDFS shell操作

    创建目录hdfs dfs -mkdir 查看所有目录hdfs dfs -ls / 上传文件hdfs dfs -pu...

  • Binary Tree(2)

    BFS vs DFS for Binary Tree What are BFS and DFS for Binar...

  • Clone Graph (Leetcode 133)

    DFS Approach: 注意,对于DFS,对map的赋值要在DFS loop开始以前。这样可以避免由于grap...

  • hdfs的命令行使用

    语法:hdfs dfs 参数 hdfs dfs -ls / 查看根路径下面的文件或文件夹 hdfs dfs -mk...

  • DFS与N皇后问题

    DFS与N皇后问题 DFS 什么是DFS DFS是指深度优先遍历也叫深度优先搜索。 它是一种用来遍历或搜索树和图数...

  • DFS及其应用

    内容概要: DFS类的实现 DFS求解连通分量 DFS求解点对之间的一个路径 DFS判定无环图和二分图 相关概念 ...

  • 684. 冗余连接

    主要掌握并查集/dfs/拓扑排序.dfs里要注意从后面开始查,特别是dfs函数如何设计以及

  • 剑指 Offer II 102. 加减的目标值

    首先想到的dfs 好家伙 1500ms。感觉差点就超时了= =。。dfs总是这样= =。。 优化写法 另类的dfs...

网友评论

      本文标题:DFS实战

      本文链接:https://www.haomeiwen.com/subject/xbriwftx.html