传送门:https://leetcode.cn/problems/find-if-path-exists-in-graph/
开一篇简单的题目用来复习以前c++ 的一些基础知识并用java实现。
题面:
题目很简单,就单纯判断起点是否有路径到终点,下面给出了三种方法去回顾了做题过程遇到(遗忘)的一些知识点。
邻接表建图 + DFS
不同于c++ 可以直接用verctor<verctor<>>
的建图方式,在java 中一般多采用 HashMap + Lis
t的方式来搭建, 这里的list 在创建的时候根据需求使用LinkedList
或者ArrayList
: ArrayList 相当于动态数组,根据下标查询时间较快;LinkedList 相当于双向链表,插入较快,两种方式都有contains的方法去判断是否有元素在列表中。
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class e_1971 {
public static void main(String[] args) {
int n = 10;
int s = 5,d = 9;
int [][] edges = {{4,3},{1,4},{4,8},{1,7},{6,4},{4,2},{7,4},{4,0},{0,9},{5,4}};
if ( validPath(n,edges,s,d) )
System.out.println("true");
else System.out.println("false");
}
public static boolean validPath(int n, int[][] edges, int source, int destination) {
Map<Integer, List<Integer>> mp = new HashMap<>();
for(int i=0;i < n; ++i)
mp.put(i,new LinkedList<>());
for(int i=0;i < edges.length; ++i){
mp.get(edges[i][0]).add(edges[i][1]);
mp.get(edges[i][1]).add(edges[i][0]);
}
if(source == destination) return true;
boolean vis[] = new boolean[n];
return dfs(source, destination ,mp, vis);
}
public static boolean dfs(int s, int d, Map<Integer, List<Integer>> mp, boolean vis[]){
if(mp.get(s).contains(d)) return true;
if(vis[s]) return false;
vis[s] = true;
for(int i=0;i < mp.get(s).size(); i++)
if(dfs(mp.get(s).get(i),d,mp,vis)) return true;
return false;
}
}
时空对比:
链式前向星 + DFS
好久没用这个了,很多槽点都忘记了。
关于无向图:
- 所需要的数组空间都开2倍
vis
数组判断边是否访问过,由于是双向边,需要通过把cnt
设置成1,在用异或取到反边,保证正向反向都被判断是否走过;当然根据个人喜好,也可以把cnt 是设置成0,然后在add
函数最后写cnt++
,也能起到一样的效果。
int cnt = 1;
// int cnt = 0;
public void add(int u, int v){
ver[++cnt] = v; //ver[cnt] = v;
nex[cnt] = head[u]; //nex[cnt++] = head[u];
head[u] = cnt;
}
.
.
.
if(mp.vis[i^1] == 0){
mp.vis[i] = mp.vis[i^1] = 1;
if(dfs(mp.ver[i], d, mp)) return true;
}
- 不同于c++, java 一般用类来保存这种结构。
public class e1971 {
static class Edge{
int N = 200007,cnt = 1;
int [] vis = new int [2*N];
int [] head = new int [N];
int [] nex = new int [2*N];
int [] ver = new int [2*N];
public void add(int u, int v){
ver[++cnt] = v;
nex[cnt] = head[u];
head[u] = cnt;
}
}
public static void main(String[] args) {
int n = 10,s = 5,d = 9;
int [][] edges = {{4,3},{1,4},{4,8},{1,7},{6,4},{4,2},{7,4},{4,0},{0,9},{5,4}};
if ( validPath(n,edges,s,d) )
System.out.println("true");
else System.out.println("false");
}
public static boolean validPath(int n, int[][] edges, int source, int destination) {
Edge mp = new Edge();
for(int i=0;i < edges.length; ++ i){
mp.add(edges[i][1], edges[i][0]);
mp.add(edges[i][0], edges[i][1]);
}
return dfs(source,destination, mp);
}
public static boolean dfs(int s, int d, Edge mp){
if(s == d) return true;
for(int i = mp.head[s]; i != 0 ; i = mp.nex[i])
if(mp.vis[i^1] == 0){
mp.vis[i] = mp.vis[i^1] = 1;
if(dfs(mp.ver[i], d, mp)) return true;
}
return false;
}
}
时空对比:
并查集
模板题,操作统统写到类里面,值得注意的是,在构建find() 函数的时候,若使用三目运算符并且是JDK7 的情况下,需要把基础类型进行装箱Integer.valueOf()
。
public class e_1971_3 {
public static void main(String[] args) {
int n = 10;
int s = 5,d = 9;
int [][] edges = {{4,3},{1,4},{4,8},{1,7},{6,4},{4,2},{7,4},{4,0},{0,9},{5,4}};
if ( validPath(n,edges,s,d) )
System.out.println("true");
else System.out.println("false");
}
public static boolean validPath(int n, int[][] edges, int source, int destination) {
andcheck mp = new andcheck(n);
for(int i=0;i < edges.length; i++)
mp.merge(edges[i][0], edges[i][1]);
return mp.issame(source,destination);
}
static class andcheck{
int size;
int [] par;
public andcheck(int size){
this.size = size;
par = new int[size];
for(int i=0;i < size; i++)
par[i] = i;
}
public int find(int a){
//装箱
return (par[a] == a )?a:Integer.valueOf(par[a] = find(par[a]));
}
public void merge(int a, int b){
int fa = find(a);
int fb = find(b);
if(fa == fb) return;
par[fa] = fb;
}
public boolean issame(int a, int b){
return find(a) == find(b);
}
}
}
时空对比: