题解 | #树的直径#

树的直径

http://www.nowcoder.com/practice/a77b4f3d84bf4a7891519ffee9376df3

方法一:

思路

题目中有提到连通图,所以就想着将给的数据转换成无向图,再遍历无向图找到一条最长路径,使用邻接矩阵存储无向图。

具体步骤

  • 首先将所给数据转换成图,由所给例子可以看出,其是个无向图,故,转换成无向图时,需要将(i,j)与(j,i)同时设置为边对应的权重;
  • 以每一个节点为起点,去计算以当前起点为节点的最大路径
  • 取所有路径中的最大值,即为最长路径。
  • 代码如下:
    import java.util.*;
    /*
    * public class Interval {
    *   int start;
    *   int end;
    * }
    */
    public class Solution {
    /**
    * 树的直径
    * @param n int整型 树的节点个数
    * @param Tree_edge Interval类一维数组 树的边
    * @param Edge_value int整型一维数组 边的权值
    * @return int整型
    */
    public int solve (int n, Interval[] Tree_edge, int[] Edge_value) {
       // write code here
       int[][] graph = new int[n][n];
       // 将树的边以及权重转换成无向图
       for (int i = 0;i < Tree_edge.length;++i){
           graph[Tree_edge[i].start][Tree_edge[i].end] = Edge_value[i];
           graph[Tree_edge[i].end][Tree_edge[i].start] = Edge_value[i];
       }
       int maxVal = 0;
       // 计算每个点最长路径
       for (int i = 0; i < n; ++i){
           int temp = compute(graph,i,new boolean[n]);
           maxVal = maxVal > temp ? maxVal : temp;
       }
       // 返回最长路径
       return maxVal;
    }
    // 递归计算每一个点的最长路径
    private int compute(int[][] graph,
                       int index, boolean[] to){
       int max = 0;
       to[index] = true;
       int temp = 0;
       for (int i = 0; i < graph.length; ++i){
           // 判断两点之间是否有路径
           if (graph[index][i] != 0){
               // 判断当前点是否已经走过
               if (!to[i]){
                   to[i] = true;
                   // 计算下一个点的路径
                   temp = compute(graph,i,to)+graph[index][i];
                   max = max > temp ? max : temp;
               }
           }
       }
       return max;
    }
    }
  • 时间复杂度:,三重循环。
  • 空间复杂度:,使用二维数组存储无向图,所以空间为

    方法二:

    思路

  • 方法一的时间复杂度与空间复杂度都是比较大的,无法完全通过牛客上的所有测试用例,所以需要对其进行优化,可以考虑使用邻接表来存储无向图,并且容易想到,在计算路径过程中其实是有很多冗余计算的,所以考虑使用来减少冗余计算,降低时间复杂度。
  • 笔者又从网上查了查树的直径,发现其还是有更为简便的计算方法,在一个连通无向无环图中,以任意结点出发所能到达的最远结点,一定是该图直径的端点之一,故可以做两次DFS来计算树的直径。

    具体步骤

  • 首先,根据连通关系以及边的权重构建无向图;
  • 随机找一顶点,利用深度优先搜索找到距离该点最远的顶点remote,这里默认就是0节点。
  • 从remote顶点开始深度优先搜索找到最长路径,该路径即为直径。
  • DFS若是不熟悉的话,可以参考下面这张图:
    图片说明
  • 代码如下:
    import java.util.*;
    public class Solution {
    // 图节点类
    class Node{
       int start;
       Map<Integer,Integer> edges = new HashMap<>();
       public Node(int start){
           this.start = start;
       }
    }
    /**
    * 树的直径
    * @param n int整型 树的节点个数
    * @param Tree_edge Interval类一维数组 树的边
    * @param Edge_value int整型一维数组 边的权值
    * @return int整型
    */
    public int solve (int n, Interval[] Tree_edge, int[] Edge_value) {
       // write code here
       if(Tree_edge == null || Edge_value == null ||
          Tree_edge.length != Edge_value.length){return 0;}
       // 将树的边以及权重转换成无向图
       Map<Integer,Node> graph = trans(Tree_edge,Edge_value);
       // remote[0] 代表以0为起点的最长路径长度,
       // remote[1]代表最长路径的终点
       int[] remote = {0, 0};    
       dfs(graph, 0, -1, 0, remote);
       int[] res = {0, 0};
       dfs(graph, remote[1], -1, 0, res);
       return res[0];
    }
    // 转换函数
    private Map<Integer,Node> trans(Interval[] Tree_edge, int[] Edge_value) {
       int len = Tree_edge.length;
       Map<Integer,Node> graph = new HashMap<>();
       for (int i = 0;i < len; ++i){
           Interval edge = Tree_edge[i];
           if (!graph.containsKey(edge.start)){
               Node node = new Node(edge.start);
               node.edges.put(edge.end,Edge_value[i]);
               graph.put(node.start,node);
           }
           graph.get(edge.start).edges.put(edge.end,Edge_value[i]);
           // 无向图所以需要建两次边
           if (!graph.containsKey(edge.end)){
               Node node = new Node(edge.end);
               node.edges.put(edge.start,Edge_value[i]);
               graph.put(node.start,node);
           }
           graph.get(edge.end).edges.put(edge.start,Edge_value[i]);
       }
       return graph;
    }
    // 深度优先搜索
    private void dfs(Map<Integer,Node> graph, int from, int prev, int path, int[] res){
       Node node = graph.get(from);
       Map<Integer,Integer> edges = node.edges;
       for (Map.Entry<Integer,Integer> edge : edges.entrySet()){
           // 未曾经过
           if (edge.getKey() != prev){
               path += edge.getValue();
               if (path > res[0]){
                   res[0] = path;
                   res[1] = edge.getKey();
               }
               dfs(graph,edge.getKey(),from,path,res);
               // 回溯
               path -= edge.getValue();
           }
       }
    }
    }
    时间复杂度:,深度优先搜索的时间复杂度为
    空间复杂度:,由于采用的是邻接表的形式存储无向图,所以空间复杂度应该为节点数加边数即M+N。
数据结构算法学习 文章被收录于专栏

个人算法学习,记录自己之前学过的东西,方便复习

全部评论

相关推荐

11-18 15:57
门头沟学院 Java
最终归宿是测开:这个重邮的大佬在重邮很有名的,他就喜欢打92的脸,越有人质疑他,他越觉得爽😂
点赞 评论 收藏
分享
8 收藏 评论
分享
牛客网
牛客企业服务