leetcode之深度优先搜索刷题总结3

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode之深度优先搜索刷题总结3相关的知识,希望对你有一定的参考价值。

leetcode之深度优先搜索刷题总结3

1-找到最终的安全状态
题目链接:题目链接戳这里!!!

思路:dfs+三色标记法
根据题意,若起始节点位于一个环内,或者能到达一个环,则该节点不是安全的。否则,该节点是安全的。

我们可以使用深度优先搜索来找环,并在深度优先搜索时,用三种颜色对节点进行标记,标记的规则如下:
白色(用 0 表示):该节点尚未被访问;
灰色(用 1 表示):该节点位于递归栈中,或者在某个环上;
黑色(用 2 表示):该节点搜索完毕,是一个安全节点。

如果搜索过程中出现不安全的,即color>0且color=1的,则返回false。如果都是安全的,则标记为color=2

class Solution 
    public List<Integer> eventualSafeNodes(int[][] graph) 
        List<Integer> list = new ArrayList<>() ;
        int [] color = new int [graph.length];
        for(int i=0; i<graph.length; i++)
            if(dfs(graph,color,i))
                list.add(i) ;
            
        
        return list ;
    
    public boolean dfs(int [][]graph, int []color, int i)
        if(color[i]>0)
            return color[i]==2 ;
        

        color[i] = 1 ;
        for(int j : graph[i])
            if(!dfs(graph,color,j))
                return false ;
            
         
        color[i] = 2 ;
        return true ;
        
    


2-网络延迟时间
题目链接:题目链接戳这里!!!

思路:题目实际是求节点 K 到其他所有点中最远的距离,那么首先需要求出节点 K 到其他所有点的最短路,然后取最大值即可。
单源最短路径问题,我们使用Dijkstra算法。

首先,Dijkstra 算法需要存储各个边权,所以代码中使用了邻接矩阵 g[i][j] 存储从点 i 到点 j 的距离。若两点之间没有给出有向边,则初始化为 inf。

算法还需要记录所有点到源点的最短距离,代码中使用了 dist[i] 数组存储源点到点 i 的最短距离,初始值也全部设为 inf。由于本题源点为 K,所以该点距离设为 0。

其次,Dijkstra 算法需要标记某一节点是否已确定了最短路,在代码中使用了vis[i] 数组存储,若已确定最短距离,则值为 true,否则值为 false。

算法需要从当前全部未确定最短路的点中,找到距离源点最短的点,通过该点更新其他所有点距离源点的最短距离。

之所以 inf 设置为 Integer.MAX_VALUE / 2 ,是因为在更新最短距离的时候,要有两个距离相加,为了防止溢出 int 型,所以除以 2。

class Solution 
   public int networkDelayTime(int[][] times, int n, int k) 
       int INF = Integer.MAX_VALUE / 2 ;
       int [][]g = new int [n][n] ;
       for(int i=0; i<n; i++)
           Arrays.fill(g[i],INF) ;
       
       for(int [] t : times) //数组g存储边的权值
         g[t[0]-1][t[1]-1] = t[2] ; 
       
       int [] dist = new int [n] ; //记录源点到目标节点的最短距离
       Arrays.fill(dist,INF) ;
       dist[k-1] = 0 ;

       boolean [] vis = new boolean[n] ; //记录当前节点是否已经确定为最短路径

       for(int i=0; i<n; i++)
            int x = -1 ;
           for(int y=0; y<n; y++)
               if(!vis[y] &&(x==-1 || dist[y]<dist[x]))
                   x = y ;
               
           
           vis[x] = true ;
           for(int y=0; y<n; y++)
               dist[y] = Math.min(dist[y],dist[x]+g[x][y]) ;
           
       
       int res = Integer.MIN_VALUE ;
       for(int value : dist)
           res = Math.max(value,res) ;
       

       return res == INF ? -1 : res ;

    


3-跳跃游戏III
题目链接:题目链接戳这里!!!

思路:记忆化搜索,每次可以向两个方向搜索,如果搜索重复位置,则中止当前搜索,如果搜索的当前值为0,则标记可以跳到,即flag=true。

AC代码如下:

class Solution 
   boolean flag ;
    public boolean canReach(int[] arr, int start) 
        //记忆化搜索
        boolean [] vis = new boolean [arr.length] ;
        flag = false ;
        dfs(arr, start,  vis);
        return flag;
    
    public void dfs(int []arr, int start, boolean [] vis)
    
        if(arr[start]==0)
            flag=true ;
            return ;
        
        if(vis[start])
            return ;
        

        vis[start] = true ;
        if(start+arr[start]<=arr.length-1)
            dfs(arr,start+arr[start],vis) ;
        
        if(start-arr[start]>=0)
            dfs(arr,start-arr[start],vis) ;
        

    



4-找到所有的农场组
题目链接:题目链接戳这里!!!

思路:搜索+标记,每一轮搜索到的连通块,更新下标的最大值,即是右下角。每一次将第一次搜索的下标和右下角的下标分别存入结果集合中,如果当前下标就是右下标,则直接返回当前下标。

class Solution 
    int [] dx = 0,0,-1,1 ;
    int [] dy = 1,-1,0,0 ;
    int maxX , maxY  ;
    List<Integer> list = new ArrayList<>() ;
    public int[][] findFarmland(int[][] land) 
     
        for(int i=0; i<land.length; i++)
            for(int j=0; j<land[0].length; j++)
                if(land[i][j]==1)
                    maxX = Integer.MIN_VALUE ;
                    maxY = Integer.MIN_VALUE ;
                    list.add(i) ;
                    list.add(j) ;
                   dfs(land,i,j) ;
                   if(maxX!=Integer.MIN_VALUE)
                    list.add(maxX) ;
                   else
                       list.add(i) ;
                   
                   if(maxY!=Integer.MIN_VALUE)
                    list.add(maxY) ;
                   else
                       list.add(j) ;
                   
                
            
        
        int [][] ans = new int [list.size()/4][4] ;
        int k = 0 ;
        for(int i=0;i<ans.length; i++)
            for(int j=0; j<ans[0].length; j++)
                ans[i][j] = list.get(k) ;
                k ++ ;
            
        
        return ans ;
    
    public void dfs(int [][] land, int x, int y)
        land[x][y] = 0 ;
        for(int i=0; i<4; i++)
            int tx = x + dx[i] ;
            int ty = y + dy[i] ;
            if(tx<0 || ty<0 || tx>land.length-1 || ty>land[0].length-1)
                continue ;
            
            if(land[tx][ty]==1)
                maxX = Math.max(maxX, tx) ;
                maxY = Math.max(maxY, ty) ;
                dfs(land, tx, ty) ;
            
        
    


5-传递信息
题目链接:题目链接戳这里!!!

思路:将二维关系数组转换成有向图,从原始位置开始搜索,每次搜索step+1,如果搜索k次后,当前的编号为n-1,表示经过 k 轮传递到编号为 n-1 的小伙伴处,则ways++。

class Solution 
    int ways;
    List<List<Integer>> edges;

    public int numWays(int n, int[][] relation, int k) 
        ways = 0;
        edges = new ArrayList<List<Integer>>();
        for (int i = 0; i < n; i++) 
            edges.add(new ArrayList<Integer>());
        
        for (int[] edge : relation) 
            edges.get(edge[0]).add(edge[1]);
        
        dfs(0, 0,n,k);
        return ways;
    

    public void dfs(int index, int steps,int n, int k) 
        if (steps == k) 
            if (index == n - 1) 
                ways++;
            
            return;
        
        List<Integer> list = edges.get(index);
        for (int nextIndex : list) 
            dfs(nextIndex, steps + 1, n, k);
        
    


思路:动态规划
dp[k][n-1]表示经过 k 轮传递到编号为 n-1 的小伙伴处的方案数
递推表达式:dp[i+1][edge[1]] += dp[i][edge[0]] ;

class Solution 
    public int numWays(int n, int[][] relation, int k) 
     int [][] dp = new int [k+1][n] ;
     dp[0][0] = 1 ;
     for(int i=0; i<k; i++)
         for(int [] edge : relation)
             dp[i+1][edge[1]] += dp[i][edge[0]] ;
         
     
     return dp[k][n-1] ;

    

6-省份数量
题目链接:题目链接戳这里!!!

思路:判断每个省份是否已经搜索,若没搜索,则进行搜索,每轮搜索进行标记为已搜索,每轮搜索累加省份数量,直至搜索完成。

class Solution 
    boolean [] vis ;
    public int findCircleNum(int[][] isConnected) 
        int povince = isConnected.length ;
        int cnt = 0 ;
        vis = new boolean[povince] ;
        for(int i=0; i<povince; i++)
            if(!vis[i])
                dfs(isConnected,i,vis,povince) ;
                cnt ++ ;
            
        
        return cnt ;
    
    public void dfs(int [][] isConnected, int i, boolean[] vis, int povince)

        vis[i] = true ;
        for(int j=0; j<povince; j++)
            if(isConnected[i][j]==1 && !vis[j])
                dfs(isConnected,j,vis,povince) ;
            
        

    

7-机器人的运动范围
题目链接:题目链接戳这里!!!

思路:沿着四个方向进行搜索,如果如果当前位置满足搜索条件,就累加1,继续搜索下去,直至不能搜索。

class Solution 
    int [] dx = 0,0,-1,1 ;
    int [] dy = -1,1,0,0 ;
    int cnt ;
    boolean [][] vis ;
    public int movingCount(int m, int n, int k) 
        cnt = 0 ;
        vis = new boolean[m][n] ;
        dfs(0,0,m,n,k,vis) ;
        return cnt ;
    
    public void dfs(int i, int j, int m, int n, int k, boolean[][] vis)
        vis[i][j] = true ;
        cnt ++ ;
        for(int x=0; x<4; x++)
            int tx = i + dx[x] ;
            int ty = j + dy[x] ;
            if(tx以上是关于leetcode之深度优先搜索刷题总结3的主要内容,如果未能解决你的问题,请参考以下文章

leetcode之深度优先搜索刷题总结2

LeetCode刷题之搜索(Java)

LeetCode刷题之搜索(Java)

刷题总结1:深度优先搜索DFS

pintia刷题记录——图

面试刷题:深度优先搜索DFS | 第87期