思路:
题目的主要信息:
- n个节点,n-1条边,使之全部连通,这就是一棵树
- 树中任意节点的路径最长值,就是求树的直径
首先我们要知道一个性质:从树的根节点深度优先搜索到最远距离,再从最远距离深度优先搜索到另一最远距离就是树的直径。
方法一:两次深度优先搜索
具体做法:
我们需要用哈希表来存储树的边结构,将树看成图,以权重构建无向图,存入哈希表中。这里便是遍历u v w数组 ,将它们的信息存入哈希表中。
因为dfs下标用到了0和-1,给的数据是节点从1开始的,因此录入哈希表的时候节点序号减1.
再从顶点出发,利用dfs找到最远距离,第二次以当前最远距离再次调用dfs找到最远距离,第二个最远距离即是树的直径。
class Solution {
public:
struct edge{ //构建边的结构
int end;
int weigh;
edge(int _end, int _weigh): end(_end), weigh(_weigh){}
};
//深度优先函数
void dfs(unordered_map<int, vector<edge>>& graph, vector<long long>& res, int start, int pre, int length){
vector<edge> edges = graph[start];
for(int i = 0; i < edges.size(); i++){ //遍历与这个点相连的每一条边
if(edges[i].end != pre){
length += edges[i].weigh;
if(length > res[0]){
res[0] = length;
res[1] = edges[i].end;
}
dfs(graph, res, edges[i].end, start, length);
length = length - edges[i].weigh; //回溯
}
}
}
long long solve(int n, vector<int>& u, vector<int>& v, vector<int>& w) {
unordered_map<int, vector<edge> > graph; //用map构建图,key为顶点编号,vector中记录与之相连的边
for(int i = 0; i < w.size(); i++){ //遍历边的集合
int weigh = w[i];
edge e1 = edge(v[i] - 1, weigh); //在图中将start与end相连
if(!graph.count(u[i] - 1)){
vector<edge> temp;
graph[u[i] - 1] = temp;
}
graph[u[i] - 1].push_back(e1);
edge e2 = edge(u[i] - 1, weigh);
if(!graph.count(v[i] - 1)){
vector<edge> temp;
graph[v[i] - 1] = temp;
}
graph[v[i] - 1].push_back(e2);
}
vector<long long> length(2, 0); //第一个零表示以第0个结点为起点的最长路径长度,第二个零表示最长路径的重点序列
dfs(graph, length, 0, -1, 0); //从0结点深度优先找最远距离
vector<long long> res(2, 0);
dfs(graph, res, length[1], -1, 0); //以刚刚找到的最远距离再次深度优先找最元距离即是答案
return res[0];
}
};复杂度分析:
- 时间复杂度:
,因为使用了unordered_map,只是相当于每个节点访问一次,
- 空间复杂度:
,哈希表大小和递归栈最大深度
方法二:两次广度优先搜索
具体做法:
同理,有dfs就会有bfs,同样是两次bfs即可得到树的直径。树的bfs属于层次遍历,于是需要使用队列将子结点压入队列中,后续再依次访问。我们这里为了访问方便,使用矩阵邻接表来表示树。
class Solution {
public:
int dis[200020]; //记录路径长度
bool vist[100020]; //访问过的记录,防止重复
vector<pair<int, int> > vec[100020]; //树两个结点之间的边
int ans = 0;
int bfs(int x) //广度优先函数
{
memset(vist, 0, sizeof(vist));
memset(dis, 0, sizeof(dis));
vist[x] = 1;
queue<int> q; //bfs依赖队列,层次遍历
q.push(x);
int point = 0;
int cur_point = x; //表示当前结点,后面会更新
while(!q.empty())
{
cur_point = q.front();
q.pop();
if(dis[cur_point] >= ans) //找最远距离
{
ans = dis[cur_point];
point = cur_point;
}
pair<int,int> temp;
for(int i = 0; i < vec[cur_point].size(); i++)
{
temp = vec[cur_point][i];
if(!vist[temp.first]) //之前没有访问过的点才进入下面
{
vist[temp.first] = true; //标记访问过的结点
dis[temp.first] = temp.second + dis[cur_point]; //相邻点加上当前这条边
q.push(temp.first);
}
}
}
return point;
}
long long solve(int n, vector<int>& u, vector<int>& v, vector<int>& w) {
for(int i = 0; i < w.size(); i++){ //将给的边相连加入v中
vec[u[i]].push_back(make_pair(v[i], w[i]));
vec[v[i]].push_back(make_pair(u[i], w[i]));
}
int point = bfs(1); //两次bfs
point = bfs(point);
return ans;
}
};复杂度分析:
- 时间复杂度:
,因为使用了邻接矩阵,相当于遍历矩阵
- 空间复杂度:
,邻接矩阵的空间及其他辅助数组

京公网安备 11010502036488号