LeetCode 1971. Find if Path Exists in Graph

There is a bi-directional graph with n vertices, where each vertex is labeled from 0 to n - 1 (inclusive). The edges in the graph are represented as a 2D integer array edges, where each edges[i] = [ui, vi] denotes a bi-directional edge between vertex ui and vertex vi. Every vertex pair is connected by at most one edge, and no vertex has an edge to itself.

You want to determine if there is a valid path that exists from vertex source to vertex destination.

Given edges and the integers nsource, and destination, return true if there is a valid path from source to destination, or false otherwise.

Example 1:

Input: n = 3, edges = [[0,1],[1,2],[2,0]], source = 0, destination = 2
Output: true
Explanation: There are two paths from vertex 0 to vertex 2:
- 0 → 1 → 2
- 0 → 2

Example 2:

Input: n = 6, edges = [[0,1],[0,2],[3,5],[5,4],[4,3]], source = 0, destination = 5
Output: false
Explanation: There is no path from vertex 0 to vertex 5.

Constraints:

  • 1 <= n <= 2 * 105
  • 0 <= edges.length <= 2 * 105
  • edges[i].length == 2
  • 0 <= ui, vi <= n - 1
  • ui != vi
  • 0 <= source, destination <= n - 1
  • There are no duplicate edges.
  • There are no self edges.

这题给了一个无向的可以有环的图,问你能不能找到一个从src到dst的路径。是一道经典的图的题。图主要就是两种解法,BFS和DFS,其实跟别的BFS/DFS还挺像的,虽然刚开始DFS也没写出来,哎。其实还有union find。

不管是BFS还是DFS,首先得把这个图给表示出来。通常采用adjacency list来表示,在java里就是Map<Integer, List<Integer>>,表示一个节点和它对应的邻居们。于是第一步就是先build好这个map。

接下来需要考虑到因为这个图是可以有环的,所以需要一个visited数组来记录这个节点是否被访问过,每次访问完需要set to true,不然第二次回来的时候就死循环了。

下面就来到了BFS/DFS的环节。写完提交以后发现BFS的效率高于DFS。

DFS

和普通的DFS一样,也是通过一个helper function来进行递归,记得这里要传入visited数组和graph的表示。helper里只需要考虑我们现在拿到一个节点了,我们需要对这个节点进行什么处理。首先,如果当前节点已经是dst了,那不就找到了,直接return true就行。如果这个节点已经被visited了,就说明从这个节点出发最后回到了它自己,那这一条路径就不存在能到dst的路径,就return false(其实这里还是不太确定)。然后就是需要visit这个节点,并对它的所有邻居都进行dfs helper操作,如果任何一个的dfs helper return了true,那当前就return true。最后所有遍历完都没return,那就说明没有,直接return false。

嗯,对于dfs里面的具体操作还是有点没有特别明白,以后需要多多复习。

class Solution {
    public boolean validPath(int n, int[][] edges, int source, int destination) {
        boolean[] visited = new boolean[n];
        Map<Integer, List<Integer>> graph = new HashMap<>();

        for (int i = 0; i < edges.length; i++) {
            int vertex0 = edges[i][0];
            int vertex1 = edges[i][1];
            List<Integer> neighbor0 = graph.getOrDefault(vertex0, new ArrayList<>());
            neighbor0.add(vertex1);
            graph.put(vertex0, neighbor0);
            List<Integer> neighbor1 = graph.getOrDefault(vertex1, new ArrayList<>());
            neighbor1.add(vertex0);
            graph.put(vertex1, neighbor1);
        }

        return dfs(graph, visited, source, destination);
    }

    private boolean dfs(Map<Integer, List<Integer>> graph, boolean[] visited, int src, int dst) {
        if (src == dst) {
            return true;
        }
        if (visited[src]) {
            return false;
        }

        visited[src] = true;

        for (int i : graph.get(src)) {
            if (dfs(graph, visited, i, dst)) {
                return true;
            }
        }

        return false;
    }
}

BFS

写完DFS以后写BFS就感觉比较简单了,感觉没怎么想就哼哧哼哧写出来了。就是普通的BFS解法,用一个int queue来存放vertex,经典的先往queue里放第一个元素,然后while queue is not empty,remove queue里第一个元素,然后把这个元素对应的子元素们放进queue,并进行一些题目specific的操作。这道题的题目specific操作就是查当前节点是不是已经是dst了,如果是dst就可以直接return true了,否则要把它的邻居们都加进queue。这里还有一点需要注意,就是因为这个图是可以有环的,所以每次需要查是否已经visit过这个节点,如果是的话就不需要再加邻居了,所以我取完节点就check有没有visit,只有没有visit才进行后面的所有操作。看了别人的写法,很多都是拿到以后先判断是不是dst,再mark为true,然后对它的邻居遍历的时候只有没visit过的邻居采访进来。我觉得两种方法都可。

class Solution {
    public boolean validPath(int n, int[][] edges, int source, int destination) {
        boolean[] visited = new boolean[n];
        Map<Integer, List<Integer>> graph = new HashMap<>();

        for (int i = 0; i < edges.length; i++) {
            int vertex0 = edges[i][0];
            int vertex1 = edges[i][1];
            List<Integer> neighbor0 = graph.getOrDefault(vertex0, new ArrayList<>());
            neighbor0.add(vertex1);
            graph.put(vertex0, neighbor0);
            List<Integer> neighbor1 = graph.getOrDefault(vertex1, new ArrayList<>());
            neighbor1.add(vertex0);
            graph.put(vertex1, neighbor1);
        }

        Queue<Integer> queue = new LinkedList<>();
        queue.add(source);
        while (!queue.isEmpty()) {
            int vertex = queue.remove();
            if (!visited[vertex]) {
                visited[vertex] = true;
                if (vertex == destination) {
                    return true;
                }
                for (int i : graph.get(vertex)) {
                    queue.add(i);
                }
            }
            // option 2
            // if (vertex == destination) {
            //     return true;
            // }
            // visited[vertex] = true;
            // for (int i : graph.get(vertex)) {
            //     if (!visited[i]) {
            //         queue.add(i);
            //     }
            // }
        }

        return false;
    }
}

然后就是union find的做法,永远也记不住,不想看了,贴个之前的笔记好了:LeetCode 200. Number of Islands_wenyq7的博客-CSDN博客

和两个答案:

LeetCode - The World's Leading Online Programming Learning Platform 

LeetCode - The World's Leading Online Programming Learning Platform 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值