迭代DFS中的边缘分类

时间:2016-08-22 08:42:01

标签: algorithm depth-first-search

边缘可以根据三个类别(后边缘,树/前边缘,交叉边缘)使用递归DFS进行分类,该DFS将节点标记为未访问,发现或完成(或白色,灰色,黑色)。

我们是否可以使用算法的迭代版本对边缘进行分类(参见Depth-First Search)?

procedure DFS-iterative(G,v):
2      let S be a stack
3      S.push(v)
4      while S is not empty
5          v = S.pop()
6          if v is not labeled as discovered:
7              label v as discovered
8              for all edges from v to w in G.adjacentEdges(v) do
9                  S.push(w)

此版本仅使用两个类别,未访问和已发现。在将所有相邻节点推送到堆栈之后,我们可以将节点标记为已完成,但它不会给出预期结果。

编辑(澄清):问题是,我们可以修改上面给出的DFS的迭代版本,以便将边缘分类为树/前沿,交叉边缘和后边缘,就像通常通过采用递归版本一样节点标签/颜色的优点?

3 个答案:

答案 0 :(得分:1)

假设您使用递归版本。然后可以修改如下:

DFS(G,v):
    v.discovered = True
    for all edges from v to w in G.adjacentEdges(v) do
    if not w.discovered then
        recursively call DFS(G,w)
    v.finished = True

使用bracketing的概念,众所周知:

  • 如果边缘是未经发现的顶点,则边缘是树边缘。

  • 如果边缘是通向已发现且未完成的顶点,则边缘是向后边缘

  • 否则边缘是交叉或前沿。

所以现在唯一的问题是让它迭代。唯一的区别是我们现在需要操纵递归之前为我们做过的事情。假设每个顶点的numActiveChildren设置为0,parent设置为Nil。迭代版本可能如下所示:

DFS-iterative(G,v):
    let S be a stack
    S.push(v)
    while S is not empty do
        v = S.pop()
        if not v.discovered do
            v.discovered = True
            for all edges from v to w in G.adjacentEdges(v) do
                if w.discovered do
                    w.parent.numActiveChildren = w.parent.numActiveChildren - 1
                v.numActiveChildren = v.numActiveChildren + 1
                w.parent = v
                S.push(w)

            while v != Nil and v.numActiveChildren = 0 do
                v.finished = True
                v = v.parent
                if v != Nil do
                    v.numActiveChildren = v.numActiveChildren - 1 

答案 1 :(得分:0)

我的解决方案是使用堆栈和当前的孩子模拟递归#34;指针。

当我们查看标准DFS堆栈中的节点时,我们将检查当前子指针是否指向此节点的邻接列表的末尾。如果是,我们就完成了这个节点。如果没有,我们将把这个子节点(如果符合条件)推送到DFS堆栈而不更新当前子指针。这将允许我们稍后对这个孩子进行后期处理。

举个例子,考虑10199 - UVa Judge的旅游指南。它基本上要求我们找到关键点,这关键点取决于边缘分类。

来自Recursive Solution

void CutVertexFinder::DFS(int root) {
  for (auto&& child : graph[root]) {
    if (child == parent[root]) continue;

    if (parent[child] == -1) {
      // Tree edge
      parent[child] = root;
      entry[child] = time++;
      farthest_ancestor[child] = child;

      num_children[root]++;
      DFS(child);

      if (entry[farthest_ancestor[child]] < entry[farthest_ancestor[root]]) {
        farthest_ancestor[root] = farthest_ancestor[child];
      }
    } else if (entry[child] < entry[root]) {
      // Back edge
      if (entry[child] < entry[farthest_ancestor[root]]) {
        farthest_ancestor[root] = child;
      }
    }
  }
}

来自Iterative Solution

void CutVertexFinder::DFS(int root) {
  std::vector<int> current_child_index(N, 0);
  stack<int> S;

  S.emplace(root);
  while (!S.empty()) {
    int node = S.top();

    const int child_index = current_child_index[node];
    if (child_index >= graph[node].size()) {
      S.pop();
      continue;
    }

    int child = graph[node][child_index];
    if (child == parent[node]) {
      current_child_index[node]++;
      continue;
    }

    if (parent[child] == -1) {
      parent[child] = node;
      entry[child] = time++;
      farthest_ancestor[child] = child;

      num_children[node]++;
      S.emplace(child);
      continue;
    }

    if (parent[child] == node) {
      if (entry[farthest_ancestor[child]] < entry[farthest_ancestor[node]]) {
        farthest_ancestor[node] = farthest_ancestor[child];
      }
    } else if (entry[child] < entry[node]) {
      if (entry[child] < entry[farthest_ancestor[node]]) {
        farthest_ancestor[node] = child;
      }
    }
    current_child_index[node]++;
  }
}

正如您所看到的,迭代解决方案可能是一种过度杀伤。

答案 2 :(得分:0)

这是我在c ++中的解决方案:

std::vector<bool> visited(number_of_nodes, false);
std::vector<int> entry(number_of_nodes, 0);
std::vector<int> exit(number_of_nodes, 0);

// trace stack of ancestors
std::vector<int> trace;

int time = 1;

// u is current node that moves around
int u = nodes.front();
trace.push_back(u);
visited[u] = true;

// iterative DFS with entry and exit times
while (!trace.empty()) {
    bool found = false;
    for (auto& v : neighbours_of(u)) {
        if ((!visited[v]) && (!found)) {
            found = true; // no blockage
            u = v;
            entry[u] = time++;
            visited[u] = true;
            trace.push_back(v);
            break;
        }
    }

    if (!found) {
        trace.pop_back();
        exit[u] = time++;
        u = trace.back();
    }
}

这将获得DFS的entryexit次。可以使用这些时间根据发布的here规则对边进行分类。你也可以动态地做到这一点,虽然规则有点不同。例如,在搜索期间,如果我们遇到edge(u,v),并且entry[v]已设置但exit[v]尚未设置,则(u,v)是后沿。