问题描述
小明的实验室有N台电脑,编号1~N。原本这N台电脑之间有N-1条数据链接相连,恰好构成一个树形网络。在树形网络上,任意两台电脑之间有唯一的路径相连。
不过在最近一次维护网络时,管理员误操作使得某两台电脑之间增加了一条数据链接,于是网络中出现了环路。环路上的电脑由于两两之间不再是只有一条路径,使得这些电脑上的数据传输出现了BUG。
为了恢复正常传输。小明需要找到所有在环路上的电脑,你能帮助他吗?
输入格式
第一行包含一个整数N。
以下N行每行两个整数a和b,表示a和b之间有一条数据链接相连。
对于30%的数据,1 <= N <= 1000
对于100%的数据, 1 <= N <= 100000, 1 <= a, b <= N
输入保证合法。
输出格式
按从小到大的顺序输出在环路上的电脑的编号,中间由一个空格分隔。
样例输入
5
1 2
3 1
2 4
2 5
5 3
样例输出
1 2 3 5
代码:
public class 发现环 {
private static int[] f; // 用于并查集
public static void main(String[] args) throws NumberFormatException, IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(in.readLine());
// 用于并查集,存储每个节点的根节点,默认是自身
f = new int[n + 1];
for (int i = 1; i < f.length; i++) {
f[i] = i;
}
// 点点间的关系,因为不知道是几叉树的树状结构,所以用链表好用
List<Integer>[] tree = new List[n + 1]; // List数组,大小n+1
int start = 0;// 环的起始位置
int end = 0;// 环的结束位置
int from, to; // 用于读取数据时的起点和终点
for (int i = 0; i < n; i++) {
String[] split = in.readLine().split(" ");
from = Integer.parseInt(split[0]);
to = Integer.parseInt(split[1]);
// 当环的起点不为0时,说明环已经出现,所以后面的节点不需要存储了
if (start == 0) {
// 判断是否出现环
if (union(from, to)) {
start = from;
end = to;
}
// 判断当前节点的List是否初始化
if (tree[from] == null) {
tree[from] = new ArrayList<>();
}
if (tree[to] == null) {
tree[to] = new ArrayList<>();
}
// 将当前关系存储树中
tree[from].add(to);
tree[to].add(from);
}
}
in.close();
// 此时已经找到了环的起点和终点
int[] res = new int[n]; // 存储环的所有节点
boolean[] visited = new boolean[n + 1];// 标记每个顶点的访问情况
// 深度优先搜索查询环的所有节点
int i = dfs(tree, start, end, res, visited, 0);
Arrays.sort(res, 0, i);
for (int j = 0; j < i; j++) {
System.out.print(res[j] + " ");
}
}
// 深度优先搜索查询环的所有节点
private static int dfs(List<Integer>[] tree, int start, int end, int[] res, boolean[] visited, int i) {
if (i > 0 && start == end) {
res[i] = start;
return i + 1;
}
res[i] = start;
visited[start] = true;
for (int next : tree[start]) {
if (!visited[next]) {
int h = dfs(tree, next, end, res, visited, i + 1);
//这里要注意
if (h != 0) {
return h;
}
}
}
return 0;
}
private static boolean union(int from, int to) {
// 查找from,to的根节点
int x = find(from);
int y = find(to);
if (x == y) {
// 两个点的根节点相同,说明出现了环
return true;
}
f[y] = x;
return false;
}
private static int find(int to) {
while (f[to] != to) {
to = f[to];
}
return to;
}
}