使用while循环迭代遍历图形

时间:2016-10-01 11:16:09

标签: javascript algorithm tree graph-algorithm cytoscape.js

我正在使用cytoscape.js来创建图表。

我希望遍历节点(带方向),但仅限于那些与包含特定值的边连接的节点。

我已经像这样递归了

function traverse(node, requiredValue, visitedNodes) {
  // break if we visit a node twice
  if (visitedNodes.map(visitedNode => visitedNode.id()).includes(node.id())) {
    return visitedNodes;
  }

  // add visited node to collection
  visitedNodes.push(node);

  // select node
  node.select();

  // only travel through valid edges
  const edgesTo = node.outgoers(function () {
    return this.isEdge() && this.data('requiredValues').includes(requiredValue);
  });

  // break if no valid connected edges
  if (edgesTo.empty()) {
    return visitedNodes;
  }

  // travel through edges
  edgesTo.forEach(edge => {
    edge.select()
    return traverse(edge.target(), agent, visitedNodes);
  });
}

它似乎有用,但我不擅长算法,所以我不确定这是否是一种聪明的方法来构建它。我已经阅读了一些关于广度优先和深度优先搜索算法的内容,但我不确定它是否是我需要的那些算法。

是否可以在不使用递归的情况下遍历树?我也试过while循环,但由于它是一棵树,我认为我不能只使用

node = rootNode;

while (true) {
  // find leaving edges
  ...

  edgesTo.forEach(edge => {
    // set new node to traverse
    node = edge.target;
  }
}

因为我认为edgesTo.forEach()将完成,然后再继续while循环中的下一次迭代。我需要它通过不同的分支“同时”遍历。

我可以从http://js.cytoscape.org/#collection/algorithms看到该库有多种算法(包括bfs和dfs),但是当我想要遍历树而不是为了搜索一个特定节点时,我是否需要这些算法?

3 个答案:

答案 0 :(得分:1)

我会明确或暗示地回答你提出的几个问题:

一般的BFS和DFS算法

BFS和DFS是遍历连接图(或图的连接组件)的算法。它们允许您从特定的起始节点访问所有连接的节点(它们不会像您暗示的那样搜索特定节点,尽管它们可以以这种方式使用),并且顺序不同它们遍历图形(广度 - 第一,意味着节点的所有直接邻居在进入下一层邻居之前被访问,与深度优先相比,意味着追求一个以一个直接邻居开始并且更深入的分支,在继续到下一个分支之前,访问通过该特定邻居节点连接到起始节点的所有节点,这些节点是通过下一个直接邻居连接的所有节点。

与您的要求相关的BFS和DFS

如前所述,两种算法都访问图形的连通分量中的所有节点(可以通过遍历边缘从起始节点到达的所有节点)。由于您有不同的要求(仅使用特定值的边缘遍历),我建议您实现自己对任一算法的更改,添加此要求。你实际上已经这样做了:你的算法在某种意义上是DFS的后代/版本。

BFS和DFS以及递归

BFS does not normally include recursion并使用数据结构(队列)来实现其遍历顺序。

DFS很容易通过递归实现(并且您实现算法的直观方式非常类似)。但是你可以在没有递归的情况下实现DFS - 使用数据结构(堆栈)来实现其遍历顺序(本质上,递归使用调用堆栈作为隐式的数据结构,因此它没有那么不同,尽管递归具有更多的开销来处理与函数调用及其环境)。 您可以在wiki of DFS中看到伪代码。这是为了方便起见:

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

答案 1 :(得分:1)

BFS和DFS是一般的图遍历算法。它们不仅用于搜索某些特定节点。许多算法都将BFS和DFS作为子程序。

您的代码基本上在图表上执行DFS。您忽略了所有不需要的边缘并以深度优先的方式遍历图形的其余部分。

是的,可以使用DFS和BFS遍历图形而不递归,只使用一些特定的边。你只需要忽略不需要的边缘。

BFS

Breadth-First-Search(Graph, root):
     create empty queue Q       
     visited.put(root)
     Q.enqueue(root)                      
     while Q is not empty:             
        current = Q.dequeue()     
        for each edge that edge.startpoint == current:
             if current has required value AND edge.endpoint is not in visited:
                 Q.enqueue(edge.endpoint)
                 visited.put(edge.endpoint)

DFS

procedure DFS-iterative(G,v):
      let S be a stack
      S.push(v)
      while S is not empty:
          v = S.pop()
          if v is not labeled as discovered:
              label v as discovered
              for all edges from v to w in G.adjacentEdges(v) :
                  if edge has required value:
                       S.push(w)

答案 2 :(得分:0)

Cytoscape包含许多common graph theory algorithms开箱即用。算法甚至允许您指定要包含/调用的元素。

您可以使用.outgoers()之类的低级遍历API自行重新实现BFS遍历算法,但为什么要重新发明轮子?

BFSDFS内置于Cytoscape中:

cy.elements().stdFilter(function( ele ){
  return ele.isNode() ? includeThisNode( ele ) : includeThisEdge( ele ); // eles to include
}).bfs({
  visit: function(){ ... } // called when new ele visited
});

指定includeThisNode()includeThisEdge()以符合您应该允许遍历哪些元素的条件。

如果您的目标节点具有特定条件,则在满足这些条件时在visit()中返回true。