有向循环图遍历算法(JavaScript)

问题描述 投票:0回答:3

我有一个连通的有向循环图。任务是发现图中的每个节点,而不陷入无限循环,就像常规的树遍历算法一样。

您可以假设我已经知道从哪个节点开始才能到达有向图中的所有点,并且对于每个节点,我都有一个函数将返回它指向的节点。是否有已知的算法来查找所有节点?

主要问题实际上是避免循环,如果有一种方法可以做到这一点,而无需跟踪每个节点并将其与已遍历的节点列表进行比较,我会很高兴。

如果您需要更多详细信息,实际任务是获取 JavaScript 中每个命名函数的列表,包括作为其他对象的属性的函数。所以我尝试了类似下面的方法,因为我认为 JS 对象彼此的引用形成了一棵树(但当然它没有):

function __findFunctions(obj){
  for (var f in obj){
    // for special case of edge with self
    if (obj === obj[f]){
      continue
    }
    if (typeof obj[f] === 'function' &&
        obj.hasOwnProperty(f) &&
          // exclude native functions, we only want user-defined ones
        !(/\[(native\scode|object\sfunction)\]/i).test(obj[f].toString()) &&
          // exclude functions with __ prefix
        !(/^\s*function\s*__/).test(obj[f].toString())
       ){
      document.write(f + "<br/>" + obj[f].toString() + "<hr/>");
    }
    //alert(typeof obj[f] + "\n" + obj + "\n" + obj[f] + "\n" + f)
    __findFunctions(obj[f]);
  }
}
__findFunctions(window);

这段代码的问题是它陷入了循环。

javascript language-agnostic graph traversal
3个回答
5
投票

如果有一种方法可以做到这一点,而无需跟踪每个节点并将其与已遍历的节点列表进行比较,我会很高兴。

它可能不像检查已遍历节点的列表那么糟糕。相反,您可以为每个节点提供某种唯一的 ID:

// psuedo
id=0;
for each node
    node.id = id++;

等等

然后您可以在遍历时将每个节点的 ID 添加到哈希中:

var alreadyTraversed = {};

// Traversing a node:
alreadyTraversed[node.id] = true;

稍后,当你需要检查它是否已经被遍历过时:

if (node.id in alreadyTraversed) // It's already been traversed...

或者,对于一个非常基本的解决方案,只需在您遍历的每个对象上设置一些属性:

node._traversed = true;

// Later:
if (someNode._traversed) // already traversed.

0
投票

如果想避免循环,则需要维护已访问节点的列表。

例如

__findFunctions(obj, visited) as your signature
at start do an "in array" test for current obj and return if so.
at start add obj to the visited for subsequent traversals.

0
投票

这是一个快速答案,您可以在 codePen 上查看 https://codepen.io/vjuju/pen/PoXZJQo

const cyclic_graph = new Map([
  ["a", { dependencies: ["b", "c"] }],
  ["b", { dependencies: ["d"] }],
  ["c", { dependencies: ["d"] }],
  ["d", { dependencies: ["a"]}],
  ["e", { dependencies: ["e"] }]
]);

const f = ({ node, node_name }) => {
  console.log(node_name);
};

// For cyclic graphs
// and acyclic graphs on which
// we only want to apply the function f once
// on each node
const traverse_cyclic_graph = (graph, f) => (...node_names) => {
  // We keep a state at the graph level
  let called = new Set();

  const traverse_graph = (graph, f) => (node_name) => {
    const node = graph.get(node_name);
    if (!node || called.has(node_name)) return;
    called.add(node_name);
    node.dependencies?.map(traverse_graph(graph, f));
    f({ node, node_name });
  };

  node_names.forEach(traverse_graph(graph, f));
};

const traverse_all_cyclic_graph = (graph, f) =>
  traverse_cyclic_graph(cyclic_graph, f)(...cyclic_graph.keys());

//traverse_cyclic_graph(cyclic_graph, f)("a");
traverse_all_cyclic_graph(cyclic_graph, f);
© www.soinside.com 2019 - 2024. All rights reserved.