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实战

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