import java.util.*;
/**
* NC316 体育课测验(二)
* @author d3y1
*/
public class Solution {
private int V;
private HashSet<Integer>[] adj;
// 入度
private int[] T;
private boolean hasCircle = false;
private ArrayList<Integer> result = new ArrayList<>();
private int[] flag;
private Stack<Integer> stack = new Stack<>();
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
* 程序入口
*
* @param numProject int整型
* @param groups int整型ArrayList<ArrayList<>>
* @return int整型ArrayList
*/
public ArrayList<Integer> findOrder (int numProject, ArrayList<ArrayList<Integer>> groups) {
return solution1(numProject, groups);
// return solution2(numProject, groups);
}
/**
* bfs
* @param numProject
* @param groups
* @return
*/
private ArrayList<Integer> solution1(int numProject, ArrayList<ArrayList<Integer>> groups){
V = numProject;
T = new int[V];
adj = new HashSet[V];
for(int i = 0; i < V; i++){
adj[i] = new HashSet<>();
}
int u,v;
for(ArrayList<Integer> group: groups){
u = group.get(1);
v = group.get(0);
// u -> v 可能有自环
adj[u].add(v);
T[v]++;
}
bfs();
// 无环 输出拓扑排序序列
if(!hasCircle){
return result;
}
return new ArrayList<>();
}
/**
* 层序遍历: 有向图是否有环路
*
* 算法:
* 1 入度为0的顶点加入队列
* 2 从图中删除该顶点及其相关的边, 更新相关顶点的入度
* 3 重复步骤1和步骤2, 直到图中所有顶点都被添加到队列中 或者 无法找到入度为0的顶点为止
* 4 如果图中存在环路, 则无法进行拓扑排序(或者排序得到的数组集合不等于顶点总数), 因为环路意味着存在循环依赖(总会有入度非0的顶点)
*
* @return
*/
private void bfs(){
Queue<Integer> queue = new LinkedList<>();
for(int i=0; i<V; i++){
if(T[i] == 0){
queue.offer(i);
}
}
int count = 0;
int u;
while(!queue.isEmpty()){
u = queue.poll();
count++;
result.add(u);
for(Integer v: adj[u]){
if(--T[v] == 0){
queue.offer(v);
}
}
}
hasCircle = (count != V);
}
/**
* dfs
* @param numProject
* @param groups
* @return
*/
private ArrayList<Integer> solution2(int numProject, ArrayList<ArrayList<Integer>> groups){
V = numProject;
adj = new HashSet[V];
for(int i=0; i<V; i++){
adj[i] = new HashSet<>();
}
flag = new int[V];
int u,v;
for(ArrayList<Integer> group: groups){
u = group.get(1);
v = group.get(0);
// u -> v 可能有自环
adj[u].add(v);
}
for(int i=0 ; i<V ; i++){
if(flag[i] == 0){
dfs(i);
}
}
// 无环 输出拓扑排序序列
if(!hasCircle){
while(!stack.isEmpty()){
result.add(stack.pop());
}
return result;
}
return new ArrayList<>();
}
/**
* 递归: 有向图是否有环路
*
* 算法:
* 标记值为0: 代表搜索起点(dfs入口)
* 标记值为1: 代表搜索中(如果搜索中碰到值标记值为1的节点, 说明有环)
* 标记值为2: 代表搜索完成(搜索过程无环)
*
* @param u
*/
public void dfs(int u){
flag[u] = 1;
for(Integer v: adj[u]){
if(flag[v] == 0){
dfs(v);
if(hasCircle){
return;
}
}else if(flag[v] == 1){
hasCircle = true;
return;
}
}
flag[u] = 2;
stack.push(u);
}
}