LeetCode 1971. Find if Path Exists in Graph

文章讲述了如何解决图中是否存在从源节点到目标节点路径的问题,提出了使用BFS和DFS两种方法,以及构建邻接表来表示图。BFS在本题中的效率高于DFS。同时,文章提到了环和已访问节点的标记是解题的关键。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值