【问题标题】:List all "unique paths" to a node列出节点的所有“唯一路径”
【发布时间】:2022-01-01 00:56:17
【问题描述】:

我通过非常类似于 DAG(有向无环图)的东西来表示一个过程。此图用邻接表表示,但不像“常规”邻接表,差别不大:

  • 表中的每个条目都是一个列表列表,
  • 每个“内部”列表都说明了所需的前驱节点。

这种数据结构的想法是在流程中保存步骤的要求。因此,例如:

P = {1:[[]], 2:[[1]], 3:[[2]], 4:[[3]], 5:[[2]], 6:[[]], 7: [[4,6],[8,5]], 8:[[]]}

对于流程 P,步骤 1 不需要任何前任,步骤需要步骤 1,...,步骤 6 也不需要任何前任,步骤 7 需要步骤(4 和 6)或(8 和 5) .

每个步骤都有一个状态(一些 ID 引用),用于确定下一步是否可以执行,或者进程是否可以终止。

在上面的示例中,如果步骤 1 不满足与步骤 5 相同的 状态 的某些特定条件,我将无法执行步骤 2,这需要步骤 2 与 state=特定的东西。而对于第 7 步,执行它的唯一方法是,如果第 4&6 或 5&8 步有它们对应的状态=特定的东西。

我需要一种方法来获取通向某个步骤的所有唯一路径,以便稍后我可以检查这些路径是否满足条件。对于第 7 步,它将是: paths = [[1,2,3,4,6],[1,2,5,8]]

我已经检查过:

大部分信息都指向某种修改后的 DFS 或某种增强的 Dijkstra。对于我已经检查和测试的内容,以上都没有给我我需要的东西,即所有“唯一路径”的列表,这些路径导致可以从“不同路径”到达的节点。

问题不是特定于语言的,因此任何语言的任何示例都将不胜感激:)

编辑:22 年 4 月 1 日 进一步说明:

  • 这些步骤是一种方式,这意味着节点 1 连接到步骤 2 的距离为 1,连接到步骤 3 的距离为 2,依此类推。但是步骤/节点 1 与 6 或 8 没有连接。
  • 所有图表都有唯一的起点和终点。在示例 1 和 7 中。
  • 是的,节点 5 应该连接到节点 7。Img 已更新。
  • 节点数将始终为

【问题讨论】:

  • 是否存在从节点 1 到图中所有其他节点的路径?我们可以假设您想要的每条路径都从节点 1 开始吗?
  • 我不确定我是否理解这里的实际问题:如果您的数据结构已经是您所显示的,那么这只是几乎不做任何工作的直接组合:对于每个节点,检查每个父节点,并在您浏览图表时将这些路径保存在跟踪数据结构中。例如。从 7 点开始:我们知道它的父母,所以对于每一个你循环他们的父母,并继续这样做。每次超过 1 条时,您都会复制迄今为止的已知路径并传递一个不同的副本作为查找的一部分,因此一旦您遍历了整个相关图表,您最终会得到一整套不同的路径。
  • 但请注意,7 绝对不是[[1,2,3,4,6],[1,2,5,8]],即使您忘记将箭头从 5 画到 8,并且忘记将 8 写为 8:[[5]]。现在,根据您的显示,7 是[[1,2,3,4],[6],[8]]。到达那里的三种方式,一条长路径,两条单节点路径。
  • "7: [[4,6],[8,5]]," 这与您的图片不符。图中5和7之间没有联系。哪个是正确的,图片还是文字?
  • @Mike'Pomax'Kamermans 我已经更新了问题并更正了图像。我认为这应该使用递归来解决,但我似乎无法弄清楚。如何跟踪堆栈中的每个不同路径?或者我如何打破或拆分递归中的两条不同路径?

标签: node.js graph-theory depth-first-search directed-acyclic-graphs


【解决方案1】:

您的图表有多大?您的性能要求是什么?

对于像您的示例这样的小图,Dijsktra 几乎是即时的。所以你不需要存储所有的路径。

  • 将所有链接的成本设置为 1
  • 将指向未处于所需状态的节点的链接成本设置为 10^10
  • 运行 Dijkstra 以通过处于所需状态的节点查找从源到目标的最短路径。

【讨论】:

    【解决方案2】:

    我想我已经设法得到了我需要的东西,但我认为答案过于复杂。

    使用所有可能路径填充跟踪器对象的功能。

    const tracker = {};
    function getPaths (step, branchRef) {
        const currentStepRequires = getStepRequires(step); // func that gets the array of arrays of current step
        const oldBranchRef = branchRef;
        const hasBranches = currentStepRequires.length > 1;
        for (const branch of currentStepRequires) {
            if (branch.length === 0) {
                return;
            }
            if (!hasBranches && !branchRef) {
                tracker[branch] = [];
            }
            if (!branchRef) branchRef = branch;
            if (hasBranches) {
                if (oldBranchRef && oldBranchRef !== branchRef) {
                    tracker[branch] = [...tracker[oldBranchRef]];
                }
                else if (tracker[branchRef]) {
                    tracker[branch] = [...tracker[branchRef]];
                    branchRef = branch;
                }
                else {
                    tracker[branch] = [];
                }
            }
    
            for (const step of branch) {
                tracker[branchRef].push(step);
                getPaths(step, branchRef);
            }
            if (hasBranches) branchRef = '';
        }
    }
    
    

    填充跟踪器对象后,我需要删除其他路径中包含的路径。 我在这里使用 lodash 来简化过滤、检查和添加路径

    const paths = [];
    _.forEach(_.sortBy(tracker, path => path.length * -1), branch => {
        const isSubpath = _.some(paths, path => _.isEqual(branch, _.intersection(path, branch)));
        if (!isSubpath) {
            paths.push(branch);
        }
    });
    
    

    对于上面的示例,这将返回以下内容: [[4,3,2,1,6], [8,5,2,1]]

    我还测试了更多“分支”,例如: P = {1:[[]], 2:[[1]], 3:[[2]], 4:[[3]], 5:[[2]], 6:[[]], 7: [[4,6],[8],[5]], 8:[[6],[3]]} 返回: [[4,3,2,1,6],[8,6],[8,3,2,1],[5,2,1]]

    目前它的工作,但是....正如我所说,我认为它比它需要的更复杂。因此,欢迎任何改进。

    【讨论】:

      猜你喜欢
      • 2022-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多