首先使用拓扑排序处理无向图的方法,去掉所有不成环的枝叶,就会出现类似示例1的情况;
观察到数据量只有 1000 ,可以接受
n
2
n^2
n2 的复杂度。对剩下的每个点,使用bfs来算出,从当前点出发又回到当前点的最短距离,也就是该点所处的最小环大小;
记住细节,把拓扑排序的访问数组 vis 传给 bfs ,防止遍历已经被剪掉的枝叶,同时 bfs 的队列的节点不止维护当前被遍历的点,还要记住这个点来自哪个父节点,防止往回走,形成一个不合法的长度为 1 的答案;
class Solution {
List<Integer>[] child;
public int bfs(int begin,boolean[] vis){
int[] v = new int[child.length];
Queue<int[]> q = new ArrayDeque();
q.offer(new int[]{begin,-1});
Arrays.fill(v,-1);
v[begin] = 0;
int len = 1;
int ret = 0;
loop : while(!q.isEmpty()){
int size = q.size();
for(int i = 0;i < size;i++){
int[] tmp = q.poll();
int now = tmp[0];
int fa = tmp[1];
for(int c : child[now]){
if(vis[c]) continue;
if(v[c] != -1){
// if(begin == 0)
// System.out.println(c + " " + v[c] + " " + now + " " + v[now]);
if(c != fa){
ret = v[c] + v[now] + 1;
break loop;
}else continue;
}
v[c] = len;
q.offer(new int[]{c,now});
}
}
len++;
}
return ret;
}
public int findShortestCycle(int n, int[][] edges) {
int[] cnt = new int[n];
child = new List[n];
for(int i = 0;i < n;i++) child[i] = new ArrayList();
for(int[] e : edges){
cnt[e[0]]++;
cnt[e[1]]++;
child[e[0]].add(e[1]);
child[e[1]].add(e[0]);
}
Queue<Integer> q = new ArrayDeque();
boolean[] vis = new boolean[n];
int remain = n;
for(int i = 0;i < n;i++)
if(cnt[i] <= 1){
vis[i] = true;
q.offer(i);
remain--;
}
while(!q.isEmpty()){
int id = q.poll();
for(int c : child[id]){
if(vis[c]) continue;
if(--cnt[c] <= 1){
vis[c] = true;
q.offer(c);
remain--;
}
// System.out.println(c + " " + remain);
}
}
if(remain == 0) return -1;
int res = Integer.MAX_VALUE;
for(int i = 0;i < n;i++){
if(!vis[i]) res = Math.min(res,bfs(i,vis));
}
return res;
}
}
另外,我们发现一个环内的每个点,会被重复计算多次这点可以优化,几乎
n
2
n^2
n2 可以优化为
n
n
n,在找成环的最终两点的过程中,持续记录每个点的父节点,找到后再回溯对每个父节点进行标记,表示这个同一个环的点可以不用再次求解。
注意:和访问数组一样,在入队列的时候,就把访问标记和父亲标记给写好,不然很容易出问题;
class Solution {
List<Integer>[] child;
boolean[] pass;
public int bfs(int begin,boolean[] vis){
int[] v = new int[child.length];
int[] f = new int[child.length];
Queue<int[]> q = new ArrayDeque();
q.offer(new int[]{begin,-1});
f[begin] = -1;
Arrays.fill(v,-1);
v[begin] = 0;
pass[begin] = true;
int len = 1;
int ret = 0;
int[] pair = new int[2];
loop : while(!q.isEmpty()){
int size = q.size();
for(int i = 0;i < size;i++){
int[] tmp = q.poll();
int now = tmp[0];
int fa = tmp[1];
for(int c : child[now]){
if(vis[c]) continue;
if(v[c] != -1){
// if(begin == 0)
// System.out.println(c + " " + v[c] + " " + now + " " + v[now]);
if(c != fa){
ret = v[c] + v[now] + 1;
pair[0] = c;pair[1] = now;
break loop;
}else continue;
}
v[c] = len;
q.offer(new int[]{c,now});
f[c] = now;
}
}
len++;
}
// System.out.println(pair[0] + " " + pair[1]);
while(pair[0] != -1){
pass[pair[0]] = true;
// System.out.println(pair[0]);
pair[0] = f[pair[0]];
}
while(pair[1] != -1){
pass[pair[1]] = true;
pair[1] = f[pair[1]];
}
return ret;
}
public int findShortestCycle(int n, int[][] edges) {
int[] cnt = new int[n];
child = new List[n];
for(int i = 0;i < n;i++) child[i] = new ArrayList();
for(int[] e : edges){
cnt[e[0]]++;
cnt[e[1]]++;
child[e[0]].add(e[1]);
child[e[1]].add(e[0]);
}
Queue<Integer> q = new ArrayDeque();
boolean[] vis = new boolean[n];
int remain = n;
for(int i = 0;i < n;i++)
if(cnt[i] <= 1){
vis[i] = true;
q.offer(i);
remain--;
}
while(!q.isEmpty()){
int id = q.poll();
for(int c : child[id]){
if(vis[c]) continue;
if(--cnt[c] <= 1){
vis[c] = true;
q.offer(c);
remain--;
}
// System.out.println(c + " " + remain);
}
}
if(remain == 0) return -1;
int res = Integer.MAX_VALUE;
pass = new boolean[n];
for(int i = 0;i < n;i++){
if(!vis[i] && !pass[i]) res = Math.min(res,bfs(i,vis));
}
return res;
}
}