两点之间简单路径的非递归DFS算法

时间:2016-02-03 07:31:01

标签: algorithm

我正在寻找一种非递归的深度优先搜索算法来查找无向图中两点之间的所有简单路径(可以循环)。

我查了很多帖子,都显示了递归算法。 似乎没有人对非递归版本感兴趣。

递归版本是这样的;

void dfs(Graph G, int v, int t) 
{
   path.push(v);
   onPath[v] = true;
   if (v == t)
   {
     print(path);
   }
   else 
   {
    for (int w : G.adj(v))
    {
        if (!onPath[w])
            dfs(G, w, t);
    }
   }
  path.pop();
  onPath[v] = false;
}

所以,我试过它(非递归),但是当我检查它时,它计算错误

void dfs(node start,node end) 
{
   stack m_stack=new stack();
   m_stack.push(start);
   while(!m_stack.empty)
   {
       var current= m_stack.pop();
       path.push(current);
      if (current == end)
      {
          print(path);
      }
      else 
      {
        for ( node in adj(current))
        {
            if (!path.contain(node))
               m_stack.push(node);
        }
      }
     path.pop();
  }

测试图是:

(A,B),(B,A), (B,C),(C,B), (B,d),(d,b)中, (C,F),(F,C), (d,F),(F,d), (F,H),(H,F)。

它是无向的,这就是为什么有(a,b)和(b,a)。 如果开始和结束节点是'a'和'h',那么应该有两个简单的路径:

A,B,C,F,H

A,B,d,F,H

但该算法找不到两者。 它显示输出为:

A,B,d,F,H,

A,B,d。

堆栈成为第二条路径的开头,这就是问题所在。 请在将其更改为非递归版本时指出我的错误。 我们将非常感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

我认为dfs是一个非常复杂的算法,尤其是迭代形式。迭代版本最重要的部分是洞察力,在递归版本中,不仅当前节点,而且当前邻居都存储在堆栈中。考虑到这一点,在C ++中,迭代版本可能如下所示:

//graph[i][j] stores the j-th neighbour of the node i
void dfs(size_t start, size_t end, const vector<vector<size_t> > &graph) 
{

   //initialize:
   //remember the node (first) and the index of the next neighbour (second)
   typedef pair<size_t, size_t> State;
   stack<State> to_do_stack;
   vector<size_t> path; //remembering the way
   vector<bool> visited(graph.size(), false); //caching visited - no need for searching in the path-vector 


   //start in start!
   to_do_stack.push(make_pair(start, 0));
   visited[start]=true;
   path.push_back(start);

   while(!to_do_stack.empty())
   {
      State &current = to_do_stack.top();//current stays on the stack for the time being...

      if (current.first == end || current.second == graph[current.first].size())//goal reached or done with neighbours?
      {
          if (current.first == end)
            print(path);//found a way!

          //backtrack:
          visited[current.first]=false;//no longer considered visited
          path.pop_back();//go a step back
          to_do_stack.pop();//no need to explore further neighbours         
      }
      else{//normal case: explore neighbours
          size_t next=graph[current.first][current.second];
          current.second++;//update the next neighbour in the stack!
          if(!visited[next]){
               //putting the neighbour on the todo-list
               to_do_stack.push(make_pair(next, 0));
               visited[next]=true;
               path.push_back(next);
         }      
      }
  }
}

不保证它没有错误,但我希望你得到要点,至少它在你的例子中找到了两条路径。

答案 1 :(得分:0)

路径计算完全错误。在处理它的邻居之前弹出最后一个节点。您的代码应该只输出最后一个节点。

最简单的解决方法是信任编译器,以便充分优化递归解决方案,使其无关紧要。您可以通过不在调用之间传递大对象以及避免每次调用分配/释放多个对象来提供帮助。

简单的解决方法是将整个路径存储在堆栈中(而不仅仅是最后一个节点)。

更难解决的问题是堆栈上有两种类型的节点。插入和删除。当您到达插入节点x值时,首先添加删除节点x,然后推送到所有邻居y的堆栈插入节点y。当您点击删除节点x时,您需要从路径中弹出最后一个值(x)。这更好地模拟了递归解决方案的动态。

更好的解决方法是只进行广度优先搜索,因为这样更容易以迭代方式实现。