寻找图中是否存在路径【LC1971】
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 n, source, and destination, return true if there is a valid path from source to destination, or false otherwise*.*
2022/12/19
开始补昨天的
- 思路:使用BFS寻找是否存在目的地到终点的有效路径,从source开始按照层次依次遍历每一层的顶点,判断是否可以达到destination
- 实现:
。首先构建邻接表adList,然后使用队列存储最近访问过的顶点,并使用boolean数组isUsed记录每个顶点的访问状态。
。初始时将source设为已访问,并且将其加入队列。之后每次将队列中的节点出队,并将其能够到达的未访问过的节点入队。如果访问到destination,则直接返回true;如果队列为空,仍未访问到destination,则返回false
class Solution { public boolean validPath(int n, int[][] edges, int source, int destination) { boolean[] isUsed = new boolean[n]; List<Integer>[] adList = new List[n]; Deque<Integer> deque = new LinkedList<>(); // 初始化邻接表 for (int i = 0; i < n; i++){ adList[i] = new ArrayList<Integer>(); } for (int[] edge : edges){ int u = edge[0], v = edge[1]; adList[u].add(v); adList[v].add(u); } // 添加起点 deque.add(source); isUsed[source] = true; // BFS搜索 while (!deque.isEmpty()){ int u = deque.pollFirst(); if (u == destination){ return true; } for (Integer next : adList[u]){ if (!isUsed[next]){ deque.addLast(next); isUsed[next] = true; } } } return false; } }
。复杂度分析
- 时间复杂度:O(n+m),n 为图中顶点的数目,m为图中边的数目。对于图中的每个顶点或者每条边,我们最多只需访问一次,因此时间复杂度为O(n+m)。
- 空间复杂度:O(n+m),空间复杂度主要取决于邻接表、记录每个顶点访问状态的数组和队列,邻接顶点列表需要的空间为O(n+m),记录访问状态需要 O(n)的空间,进行广度优先搜索时队列中最多只有 n个元素,因此总的空间复杂度为O(n+m)。
并查集
- 思路:使用并查集判断两个节点的连通性,如果在并查集中source和destination的根相同,那么证明两个点连通,否则不连通
- 实现
。首先初始化并查集,每个节点的根节点均为自身
。然后构建并查集,使用join将每条边的两个节点合并
。最后判断source和destination的根是否相同,若相同则返回true
class Solution { private int[] father; public boolean validPath(int n, int[][] edges, int source, int destination) { // 初始化并查集 father = new int[n]; for (int i = 0; i < n; i++){ father[i] = i; } // 构建并查集 for (int[] edge : edges){ join(edge[0],edge[1]); } // 判断根是否相同 return isSame(source, destination); } private int find(int u){ if (father[u] != u){ father[u] = find(father[u]); } return father[u]; } private void join (int u, int v){ u = find(u); v = find(v); if (u == v) return; father[v] = u; } private Boolean isSame (int u, int v){ u = find(u); v = find(v); return u == v; } }
。复杂度分析
- 时间复杂度:O(n+α(m)∗m),n 为图中顶点的数目,m为图中边的数目。其中 α \alphaα为阿克曼函数的反函数,其增长极其缓慢,也就是说其单次操作的平均运行时间可以认为是一个很小的常数。
- 空间复杂度:O(n),空间复杂度主要取决于并查集,并查集需要O(n)的空间,因此总的空间复杂度为O(n)。