1021 Deepest Root (25分)
A graph which is connected and acyclic can be considered a tree. The height of the tree depends on the selected root. Now you are supposed to find the root that results in a highest tree. Such a root is called the deepest root.
Input Specification:
Each input file contains one test case. For each case, the first line contains a positive integer N (≤104) which is the number of nodes, and hence the nodes are numbered from 1 to N. Then N−1 lines follow, each describes an edge by given the two adjacent nodes’ numbers.
Output Specification:
For each test case, print each of the deepest roots in a line. If such a root is not unique, print them in increasing order of their numbers. In case that the given graph is not a tree, print Error: K components
where K
is the number of connected components in the graph.
Sample Input 1:
5
1 2
1 3
1 4
2 5
Sample Output 1:
3
4
5
Sample Input 2:
5
1 3
1 4
2 5
3 4
Sample Output 2:
Error: 2 components
题目大意:
给定N个节点,N-1条边。理想的情况下会形成一棵”树“。这里指的树 它的边是不带方向的。因此我们把树中的任意一个节点作为根节点,都会得到一个树的高度,或者说树的层数。(层序遍历得到的层数)题目要求我们求出 ,能够使得树的层数最多的跟节点是谁,如果有多个,那么按照节点的ID大小进行输出。 当然不理想的情况下,N个顶点 N-1条边不能够得到一颗树。 比如下面这个例子: 如果得不到一棵树,那么就需要输出图的联通分量的个数。
// 这是一个示意图,O代表一个顶点,可以看到这里有5个顶点 4条边,但是没有形成一棵树,联通分量为2
O------O
O------O
| /
| /
O
思路分析:
首先我使用的是邻接矩阵存储图,然后使用 DFS深度优先遍历先求出这个图的联通分量的个数。如果不是1 , 那么这幅图就不是一棵树,输出题目要求的信息Error:xx components
如果是一棵树,开始下一步:对每一个顶点进行一次求解,求解以它们为根 的树的层数, 求解层数使用层序遍历。 把每一个顶点和求得的层数用一个pair 进行封装。放入一个list 列表中,最后对列表进行排序。输出层数最多的几个顶点编号,最大的有几个输出几个。
使用邻接矩阵存储会导致一个测试点内存超限。
我在第二版代码中改用 list 邻接表的写法,不过还是内存超限,最后想到可能是因为ArrayList 采用动态数组,有一定的扩容机制, 对内存的使用还是多了。于是我在第三版中改用了LinkedList 这次内存没问题了,但是一个之前的测试点还是没通过, 告知答案错误。不知道是哪里出了问题
如果您有更好的改进方式,希望能够与我分享。解决我的困惑。
完整代码:
// 使用邻接矩阵
import java.util.*;
//一个测试用例内存超限
public class p1021 {
static class Pair { // pair 用于存放一个节点的ID 和以这个节点为root 的整棵树的高度
int index;
int depth;
public Pair(int index, int depth) {
this.index = index;
this.depth = depth;
}
}
static boolean[] used;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();
int[][] node = new int[num][num];
for (int i = 0; i < num - 1; i++) { //用邻接矩阵存储整张图
int num1 = scanner.nextInt();
int num2 = scanner.nextInt();
node[num1 - 1][num2 - 1] = 1;
node[num2 - 1][num1 - 1] = 1;
}
int sum = GetComp(node); // 得到连通分量个数
if (sum != 1) { //题目给的是N个顶点N-1条边,如果连通分量是1 必然可以形成一棵树
System.out.printf("Error: %d components", sum);
return;
}
List<Pair> list = new ArrayList<>();
for (int i = 0; i < node.length; i++) { // 存储每一个顶点 和 以它为根的树的高度
list.add(new Pair(i + 1, BFS(node, i)));
}
Collections.sort(list, new Comparator<Pair>() {
@Override
public int compare(Pair o1, Pair o2) {
if (o1.depth > o2.depth)
return -1;
else if (o1.depth < o2.depth)
return 1;
else {
if (o1.index > o2.index)
return 1;
else if (o1.index < o2.index)
return -1;
else
return 0;
}
}
}); // 对求得的所有结果进行排序
int dep = list.get(0).depth; // 经过排序后 最大的高度就在0号位置
for (int i = 0; i < list.size(); i++) { // 我们只要输出最大高度的节点编号
if (list.get(i).depth == dep)
System.out.println(list.get(i).index);
else
break;
}
}
//求解连通分量的个数 使用DFS遍历
static int GetComp(int[][] num) {
used = new boolean[num.length];
int comp = 0;
for (int i = 0; i < num.length; i++) {
if (!used[i]) {
DFS(num, i);
comp++;
}
}
return comp;
}
// DFS遍历
static void DFS(int[][] num, int i) {
used[i] = true;
for (int j = 0; j < num.length; j++) {
if (num[i][j] == 1 && !used[j])
DFS(num, j);
}
}
// BFS 遍历,求解从start这个顶点出发,图(树)有多少层
static int BFS(int[][] num, int start) {
boolean[] visit = new boolean[num.length];// 这个地方是有必要的,因为是用邻接矩阵存储无向图, 矩阵是对称的 如果不对访问过的节点做标记 很容易死循环
Queue<Integer> queue = new LinkedList<>();
int depth = 0;
queue.offer(start);
while (queue.size() != 0) {
int size = queue.size();
depth++;
for (int i = 0; i < size; i++) { //每一次循环会迭带一整层节点
int node = queue.poll();
visit[node] = true;
for (int j = 0; j < num.length; j++) {
if (num[node][j] == 1 && !visit[j])
queue.offer(j);
}
}
}
return depth;
}
}
// 使用邻接表
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
//一个测试用例内存超限
public class p1021 {
static class Pair { // pair 用于存放一个节点的ID 和以这个节点为root 的整棵树的高度
int index;
int depth;
public Pair(int index, int depth) {
this.index = index;
this.depth = depth;
}
}
static boolean[] used;
static int num ; // 顶点的个数
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader( System.in)) ;
num = Integer.parseInt(bf.readLine()); // 一共有num个顶点 编号1-num
List<List<Integer>> graphs = new ArrayList<>();
for(int j = 0 ;j<=num;j++){
graphs.add(new ArrayList<>()) ; // 对存储容器进行初始化 方便一会添加元素 第0个位置空出来不用
}
for (int i = 1; i <= num-1 ; i++) { //用list<List>存储整张图就是所谓的邻接表
String [] edge= bf.readLine().split(" ") ;
int num1 = Integer.parseInt(edge[0]);
int num2 = Integer.parseInt(edge[1]);
graphs.get(num1).add(num2) ;
graphs.get(num2).add(num1) ;
}
int sum = GetComp(graphs); // 得到连通分量个数
if (sum != 1) { //题目给的是N个顶点N-1条边,如果连通分量是1 必然可以形成一棵树
System.out.printf("Error: %d components", sum);
return;
}
List<Pair> list = new ArrayList<>();
for (int i = 1; i <=num ; i++) { // 存储每一个顶点 和 以它为根的树的高度
list.add(new Pair(i , BFS(graphs, i)));
}
Collections.sort(list, new Comparator<Pair>() {
@Override
public int compare(Pair o1, Pair o2) {
if (o1.depth > o2.depth)
return -1;
else if (o1.depth < o2.depth)
return 1;
else {
if (o1.index > o2.index)
return 1;
else if (o1.index < o2.index)
return -1;
else
return 0;
}
}
}); // 对求得的所有结果进行排序
int dep = list.get(0).depth; // 经过排序后 最大的高度就在0号位置
for (int i = 0; i < list.size(); i++) { // 我们只要输出最大高度的节点编号
if (list.get(i).depth == dep)
System.out.println(list.get(i).index);
else
break;
}
}
//求解连通分量的个数 使用DFS遍历
static int GetComp(List<List<Integer>> graphs) {
used = new boolean[num+1];
int comp = 0;
for (int i = 1; i <= num ; i++) {
if (!used[i]) {
DFS(graphs, i);
comp++;
}
}
return comp;
}
// DFS遍历
static void DFS(List<List<Integer>> graphs, int i) {
used[i] = true;
for (int j = 0; j < graphs.get(i).size(); j++) {// graphs.get(i).size() 表示的是顶点i有多少相邻的顶点
if (!used[ graphs.get(i).get(j)]) //然后我们去取这个顶点, graphs.get(i).get(j) java 写起来比较啰嗦
DFS(graphs, graphs.get(i).get(j));
}
}
// BFS 遍历,求解从start这个顶点出发,图(树)有多少层
static int BFS(List<List<Integer>> graphs, int start) {
boolean[] visit = new boolean[num+1];// 这个地方是有必要的,因为是用邻接矩阵存储无向图,矩阵是对称的 如果不对放问过的节点做标记 很容易死循环
Queue<Integer> queue = new LinkedList<>();
int depth = 0;
queue.offer(start);
while (queue.size() != 0) {
int size = queue.size();
depth++;
for (int i = 0; i < size; i++) { //每一次循环会迭带一整层节点
int node = queue.poll();
visit[node] = true;
int verNum = graphs.get(node).size();
for (int j = 0; j <verNum ; j++) {
if (!visit[graphs.get(node).get(j)])
queue.offer(graphs.get(node).get(j));
}
}
}
return depth;
}
}
// 使用linkedList 之前内存超限的地方显示答案错误
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class p1021 {
static class Pair { // pair 用于存放一个节点的ID 和以这个节点为root 的整棵树的高度
int index;
int depth;
public Pair(int index, int depth) {
this.index = index;
this.depth = depth;
}
}
static boolean[] used;
static int num ; // 顶点的个数
public static void main(String[] args) throws IOException {
BufferedReader bf = new BufferedReader(new InputStreamReader( System.in)) ;
num = Integer.parseInt(bf.readLine()); // 一共有num个顶点 编号1-num
List<List<Integer>> graphs = new LinkedList<>(); // 使用链式存储
for(int j = 0 ;j<=num;j++){
graphs.add(new LinkedList<>()) ; // 对存储容器进行初始化 方便一会添加元素 第0个位置空出来不用
}
for (int i = 1; i <= num-1 ; i++) { //用list<List>存储整张图就是所谓的邻接表
String [] edge= bf.readLine().split(" ") ;
int num1 = Integer.parseInt(edge[0]);
int num2 = Integer.parseInt(edge[1]);
graphs.get(num1).add(num2) ;
graphs.get(num2).add(num1) ;
}
int sum = GetComp(graphs); // 得到连通分量个数
if (sum != 1) { //题目给的是N个顶点N-1条边,如果连通分量是1 必然可以形成一棵树
System.out.printf("Error: %d components", sum);
return;
}
List<Pair> list = new LinkedList<>();
for (int i = 1; i <=num ; i++) { // 存储每一个顶点 和 以它为根的树的高度
list.add(new Pair(i , BFS(graphs, i)));
}
Collections.sort(list, new Comparator<Pair>() {
@Override
public int compare(Pair o1, Pair o2) {
if (o1.depth > o2.depth)
return -1;
else if (o1.depth < o2.depth)
return 1;
else {
if (o1.index > o2.index)
return 1;
else if (o1.index < o2.index)
return -1;
else
return 0;
}
}
}); // 对求得的所有结果进行排序
int dep = list.get(0).depth; // 经过排序后 最大的高度就在0号位置
for (int i = 0; i < list.size(); i++) { // 我们只要输出最大高度的节点编号
if (list.get(i).depth == dep)
System.out.println(list.get(i).index);
else
break;
}
}
//求解连通分量的个数 使用DFS遍历
static int GetComp(List<List<Integer>> graphs) {
used = new boolean[num+1];
int comp = 0;
for (int i = 1; i <= num ; i++) {
if (!used[i]) {
DFS(graphs, i);
comp++;
}
}
return comp;
}
// DFS遍历
static void DFS(List<List<Integer>> graphs, int i) {
used[i] = true;
int verNUm = graphs.get(i).size();
for (int j = 0; j < verNUm; j++) {// graphs.get(i).size() 表示的是顶点i有多少相邻的顶点
if (!used[ graphs.get(i).get(j)]) //然后我们去取这个顶点, graphs.get(i).get(j) java 写起来比较啰嗦
DFS(graphs, graphs.get(i).get(j));
}
}
// BFS 遍历,求解从start这个顶点出发,图(树)有多少层
static int BFS(List<List<Integer>> graphs, int start) {
boolean[] visit = new boolean[num+1];// 这个地方是有必要的,因为是用邻接矩阵存储无向图,矩阵是对称的 如果不对放问过的节点做标记 很容易死循环
Queue<Integer> queue = new LinkedList<>();
int depth = 0;
queue.offer(start);
while (queue.size() != 0) {
int size = queue.size();
depth++;
for (int i = 0; i < size; i++) { //每一次循环会迭带一整层节点
int node = queue.poll();
visit[node] = true;
int verNum = graphs.get(node).size();
for (int j = 0; j <verNum ; j++) {
if (!visit[graphs.get(node).get(j)])
queue.offer(graphs.get(node).get(j));
}
}
}
return depth;
}
}