如何在树上做DFS? (不一定是二进制)

时间:2016-01-23 03:36:01

标签: c++ algorithm graph graph-algorithm depth-first-search

我有一个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 22 0。因此,当我在dfs处启动2时,我将节点0添加到我的堆栈中,这是错误的。

我尝试了另一种只插入边缘的方法,但也没有用,因为在示例中我会插入3 2,这是从3到节点{{1}的边缘当我在节点2启动dfs时,我将无法访问节点2

我觉得问题很简单,我错过了一些重要的想法!

1 个答案:

答案 0 :(得分:1)

由于您的图是根树,因此您可以执行以下预处理。

  1. 从root(顶点#0);
  2. 启动DFS
  3. 对于每个顶点 u ,请存储其 v 。这意味着如果您沿着从根到 u 的最短路径行进,此路径上倒数第二个顶点将是 v 。请注意,在任何树中,从一个顶点到另一个顶点只有一条最短路径。我们假设您有一个数组parent,根据上面的定义,parent[u] = vparent[0] = -1。{
    您可以通过注意来计算parent数组,如果您执行s.push(g[v][i]),则vi的父项(否则您将首先访问i) ;
  4. 由于parent[v]是来自全局根(顶点0)的最短路径上的前一个顶点,因此它也是来自任何顶点 x 的最短路径上的前一个顶点,其中包含 v 在其子树中。
  5. 现在,当您想要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来算法推断方向。