收集树上所有苹果的最少时间
题目描述
分析
这是一个n个节点的无向树,我们可以看成一个无向图的形式,先将所有的边构成一个图,注意是无向图,所以在生成图的时候一条边可以有两条路径,从0-->1 和 1-->0
。
当我们按照先序遍历的时候,我们无法确定哪一个根节点是路径需要经过的点。所以需要先找到这个有苹果的节点,然后从这个节点逆向走到根节点,中间记录一下那些点走过了,因为题目中是可以攒苹果然后一起带着走到根节点的。
如果是中序遍历的话,则需要模拟一棵树的结构,就不需要无向图了。
后序遍历与邻接表存储
class Solution {
public:
int ans = 0;//路径中的点的数量,不包括根节点
int minTime(int n, vector<vector<int>>& edges, vector<bool>& hasApple) {
vector<vector<int> > map(n);
for(auto& e : edges)
{
map[e[0]].push_back(e[1]);
map[e[1]].push_back(e[0]);
}
vector<bool> visit(n);
//后序遍历找到需要经过的节点数
PostOrder(map,0,hasApple,visit);
//防止树中没有苹果
return max(ans,0) * 2;
}
bool PostOrder(vector<vector<int> >& map,int index,vector<bool>& hasApple,vector<bool>& visit)
{
//先标记当前节点
visit[index] = true;
//记录当前节点是否是需要经过的
bool flag = hasApple[index];
//遍历孩子节点
for(auto son : map[index])
{
if(visit[son]) continue;
//判断这个孩子节点是否是需要经过的节点,采用后序的思想
if(PostOrder(map,son,hasApple,visit))
{
flag = true;
ans++;
}
}
return flag;
}
};
链式存储,参考大佬程序完善后
class Solution {
public:
struct edge
{
int to;
int next;
};
edge e[200005];//存放每一条边
int head[200005];//边的开始节点的位置
int f[200005];//当前点的父亲节点位置
int ans = 0;
//链式添加一条边
void add(int from,int to,int len)
{
e[len].to = to;
e[len].next = head[from];
head[from] = len;
}
//搜索,找出需要经过的点,包括根节点
void dfs(vector<int>& visit,int x)
{
//遍历以当前点为起点,可以达到的所有边
for(int i = head[x]; i; i = e[i].next)
if(e[i].to != f[x])//并查集,防止重复搜索
{
f[e[i].to] = x;
dfs(visit,e[i].to);
visit[x] += visit[e[i].to];
}
//记录可以到达的点
if(visit[x]) ans++;
}
int minTime(int n, vector<vector<int>>& edges, vector<bool>& hasApple) {
int len = 0;
for(auto& eoch : edges)
{
add(eoch[1],eoch[0],len++);
add(eoch[0],eoch[1],len++);
}
vector<int> visit(200005);
for(int i = 0; i < n; i++)
visit[i]= hasApple[i];
dfs(visit,0);
return max(ans,1)*2 - 2;
}
};