如何从没有目的地的给定节点创建路径

时间:2018-09-23 04:24:42

标签: javascript algorithm graph-theory depth-first-search breadth-first-search

我发现了棋子的可能移动并将其存储在以下数组中。

calculate

现在,我需要根据这些动作创建以下路径。

var moves = [ {from: 67 , to:35} , {from: 35 , to:3} , {from: 35 , to:37} , {from: 35 , to:33} , {from: 37 , to:5} , {from: 37 , to:39} , {from: 33 , to:1} ,{from: 39 , to:7} ] ; 

我做了一些代码来创建路径数组,但这不是我需要做的工作,请有人帮忙创建路径。

因为没有目的地,所以我不能使用DFS或BFS。

var path1= [{from:67, to:35} , {from:35, to:3}];
var path2= [{from:67, to:35} , {from:35, to:37} ,  {from:37, to:5} ];
var path3= [{from:67, to:35} , {from:35, to:33} ,  {from:33, to:1}];
var path4= [{from:67, to:35} , {from:35, to:37} ,  {from:37, to:39} ,  {from:39, to:7} ];

以下答案适用于以上示例,但不适用于以下示例

84至52、52至20、52至54、52至50、20至22、20至18、54至22 50至18、22至20、18至20、20至18、20至22

具有以下路径

1)84 52,52 20,20 18

2)84 52,52 20,20 22

3)84 52、52 54、54 22、22 20、20 18

4)84 52,52 50,50 18,18 20,20 22

为此图。

enter image description here

1 个答案:

答案 0 :(得分:0)

第一个示例是一个tree traversal问题:找到为其所有叶子(没有子代的后代节点)指定根的每条路径。

要执行遍历,这有助于将树放置成一种形式,从而可以快速查找给定父级的子级。搜索之后,我们需要将结果路径转换为您要求的格式。

这是树:

     67
     |
     35
   / | \  
  3  37 33
    / \   \
   5   39  1
        \
         7

解决方案与DFS / BFS相同,除了找到叶子节点后​​不返回,计算返回到根的路径,将其添加到主路径列表中并继续搜索树的其余部分,这一解决方案与之相同。 / p>

const pathsToLeaves = (root, tree) => {
  const parent = {root: null};
  const stack = [root];
  const paths = [];
  
  while (stack.length) {
    let curr = stack.pop();
    
    if (curr in tree) {
      for (const child of tree[curr]) {
        stack.push(child);
        parent[child] = curr;
      }
    }
    else {
      const path = [];
      
      while (curr) {
        path.unshift(curr);
        curr = parent[curr];
      }
      
      paths.push(path);
    }
  }
  
  return paths;
};


const movesToTree = moves => 
  moves.reduce((a, e) => {
    if (!(e.from in a)) {
      a[e.from] = [];
    }

    a[e.from].push(e.to);
    return a;
  }, {})
;

const pathsToMoves = paths => 
  paths.map(f => f.reduce((a, e, i) => {
    if (a === null) {
      a = [{from: e}];
    }
    else if (i < f.length - 1) {
      a[a.length-1].to = e;
      a.push({from: e});
    }
    else {
      a[a.length-1].to = e;
    }

    return a;
  }, null))
;

const getPaths = (from, moves) => 
  pathsToMoves(pathsToLeaves(from, movesToTree(moves)))
;

const moves = [
  {from: 67, to: 35}, 
  {from: 35, to: 3}, 
  {from: 35, to: 37}, 
  {from: 35, to: 33}, 
  {from: 37, to: 5}, 
  {from: 37, to: 39}, 
  {from: 33, to: 1},
  {from: 39, to: 7}
]; 

console.log(getPaths(67, moves));

您发布的第二个示例是循环多重图。仍然可以获取您请求的所有路径,但是该算法比树形版本的效率低很多,这是因为预处理需要删除多图中的平行边,在转换为所需格式或从所需格式转换以及在复制过程中进行数组/对象复制遍历。可以使用各种方法来优化其中许多减速带,但这是一个基本版本:

const pathsFrom = (src, graph) => {
  const stack = [[src, [], {}]];
  const paths = [];
  
  while (stack.length) {
    const [curr, path, visited] = stack.pop();
    
    if (curr in graph && !(curr in visited)) {
      visited[curr] = 1;
      path.push(curr);
      let pathFollowed = false;

      for (const neighbor of graph[curr]) {
        if (!(neighbor in visited)) {
          pathFollowed = true;

          const visitedCpy = Object.keys(visited).reduce((a, e) => {
            a[e] = visited[e];
            return a;
          }, {});
          stack.push([neighbor, path.slice(0), visitedCpy]);
        }
      }

      if (!pathFollowed) {
        paths.push(path);
      }
    }
    else {
      paths.push(path.concat(curr));
    }
  }
  
  return paths;
};

const movesToGraph = moves => 
  moves.reduce((a, e) => {
    if (!(e.from in a)) {
      a[e.from] = [];
    }

    a[e.from].push(e.to);
    return a;
  }, {})
;

const pathsToMoves = paths => 
  paths.map(f => f.reduce((a, e, i) => {
    if (a === null) {
      a = [{from: e}];
    }
    else if (i < f.length - 1) {
      a[a.length-1].to = e;
      a.push({from: e});
    }
    else {
      a[a.length-1].to = e;
    }

    return a;
  }, null))
;

const dedupe = a =>
  Object.values(a.reduce((a, e) => {
    const key = `${e.from} ${e.to}`;

    if (!(key in a)) {
      a[key] = e;
    }

    return a;
  }, {}))
;

const getPaths = (from, moves) => 
  pathsToMoves(pathsFrom(from, movesToGraph(dedupe(moves)), [], []))
;

[
  [
    {from: 67, to: 35}, 
    {from: 35, to: 3}, 
    {from: 35, to: 37}, 
    {from: 35, to: 33}, 
    {from: 37, to: 5}, 
    {from: 37, to: 39}, 
    {from: 33, to: 1},
    {from: 39, to: 7}
  ],
  [
    {from: 84, to: 52}, 
    {from: 52, to: 20}, 
    {from: 52, to: 54}, 
    {from: 52, to: 50}, 
    {from: 20, to: 22}, 
    {from: 20, to: 18}, 
    {from: 54, to: 22},
    {from: 50, to: 18},
    {from: 22, to: 20},
    {from: 18, to: 20},
    {from: 20, to: 18},
    {from: 20, to: 22},
  ]
].forEach(test => console.log(getPaths(test[0].from, test)));