我有一个n
个节点的树(标记为0 to n
)。我用两个向量来保存边缘信息。
typedef std::vector<std::vector<int>> graph;
输入为n-1
边缘形式:
0 1
1 2
2 3
and so on
我被告知节点0
始终是根节点。
我使用以下方法扫描边缘:
for (int i = 0; i < n-1; i++) {
scanf("%d %d", &a, &b);
g[a].push_back(b);
g[b].push_back(a); // the second approach doesn't use this line
}
这是我简单的dfs:
void dfs(graph &g, int v) {
std::vector<int> visited; // I don't use a visited array for the second approach
for (int i = 0; i < g.size(); i++) {
visited.push_back(0);
}
std::stack<int> s;
std::set<int> t;
s.push(v);
while (!s.empty()) {
int i = s.top(); s.pop();
// do stuff
for (int i = 0; i < g[v].size(); i++) {
if (!visited[i]) {
visited[i] = true;
s.push(g[v][i]);
}
}
}
}
例如说我们有4个节点和以下边缘:
0 1
0 2
3 2
2 4
说我对从2
开始的子树感兴趣。上述方法不起作用,因为我插入了无向边0 2
和2 0
。因此,当我在dfs
处启动2
时,我将节点0
添加到我的堆栈中,这是错误的。
我尝试了另一种只插入边缘的方法,但也没有用,因为在示例中我会插入3 2
,这是从3
到节点{{1}的边缘当我在节点2
启动dfs
时,我将无法访问节点2
。
我觉得问题很简单,我错过了一些重要的想法!
答案 0 :(得分:1)
由于您的图是根树,因此您可以执行以下预处理。
parent
,根据上面的定义,parent[u] = v
和parent[0] = -1
。{parent
数组,如果您执行s.push(g[v][i])
,则v
是i
的父项(否则您将首先访问i
) ; parent[v]
是来自全局根(顶点0)的最短路径上的前一个顶点,因此它也是来自任何顶点 x 的最短路径上的前一个顶点,其中包含 v 在其子树中。现在,当您想要DFS超过顶点 u 的子树时,您可以像现在一样执行DFS,但不会访问任何顶点的父。比如说,如果你想s.push(v)
,parent[u] = v
,请不要这样做。这样您就永远不会离开 u 的子树。
实际上,知道parent
,你可以摆脱你的visited
阵列。当你&#34;做东西&#34;对于顶点 v ,已访问的 v 的唯一邻居是parent[v]
。此属性不依赖于要遍历其子树的初始顶点。 DFS代码看起来像这样(假设您已完成预处理以获取parent
:
void dfs(graph &g, vector<int> &parent, int v) {
std::stack<int> s;
s.push(v);
while (!s.empty()) {
int v = s.top(); s.pop(); // By the way, you have mistake here: int i = s.top().
// do stuff
for (int i = 0; i < g[v].size(); i++) {
if (parent[v] != g[v][i]) {
s.push(g[v][i]);
}
}
}
}
PS这种方法在某种程度上类似于你的第二种方法:它只处理从根到子树的边。但它没有缺陷,例如在你的例子中使用&#34; 3 2&#34;是错误的方向,因为你通过从root进行DFS来算法推断方向。