PAT1021 最大高度的树的根节点

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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值