【问题标题】:Non recursive DFS algorithm for simple paths between two points两点之间简单路径的非递归 DFS 算法
【发布时间】:2016-02-03 07:31:00
【问题描述】:

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

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

递归版本是这样的;

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.

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

【问题讨论】:

  • 好吧。我想找到简单的路径。
  • 你不能使用堆栈并以非递归方式进行吗?
  • 我试过了,但是修正当前路径有问题,所以第二个路径会出错。我想我在某个地方错了,所以帮助
  • 您作为答案发布的内容(代码和出了什么问题)实际上应该是您问题的一部分。另外,请正确格式化代码(每行开头四个空格)并告诉我们到底出了什么问题。 “计算错误”这句话并没有告诉我们您的预期和得到的结果。

标签: algorithm


【解决方案1】:

我认为 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);
         }      
      }
  }
}

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

【讨论】:

  • 太好了,我终于有了答案。我使用'for'语句并立即放置所有邻居,这就是为什么我一旦找到路径就无法返回。非常感谢。
  • 我认为微妙的部分是如何记住当前节点的访问邻居。看来这个算法可能会研究已经探索过的路径。好吧,非递归肯定很复杂。
【解决方案2】:

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

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

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

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

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

【讨论】:

  • “路径计算全错”,是的,我想是的,我需要一个想法来解决它。我只有一种节点。那么如何构建正确的简单路径?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-15
  • 2012-06-21
  • 1970-01-01
  • 2012-05-21
  • 2017-08-10
  • 1970-01-01
  • 2017-10-21
相关资源
最近更新 更多