递归循环遍历对象数组,以使用Javascript构建所有可能路由的数组

时间:2017-12-26 17:56:53

标签: javascript arrays json object recursion

我试图编写一个路由功能,它将从任意两个给定位置返回所有可能的路线(我称之为"空格"),但是我被卡住了写递归函数。

我的数据看起来像这样:

const allSpaces = [
    {
        id: 0,
        name: 'Living Room',
        connectedSpaces: [1,2]
    },
    {
        id: 1,
        name: 'Hallway A',
        connectedSpaces: [0,4]
    },
    {
        id: 2,
        name: 'Hallway B',
        connectedSpaces: [0,4]
    },
    {
        id: 3,
        name: 'Bedroom',
        connectedSpaces: [1,2]
    }
];

因此,调用getAllRoutes(0,3)方法将遍历所有可能的路由并返回一个数组数组:

[
    [0,1,3],
    [0,2,3]
]

请记住,这可能并不总是像我的示例那样简单化数据集(即,走廊A可能有一个提供替代路线的分支,或者可能与以前访问过的空间重新交叉)。

我很难过。我已经对递归函数进行了多次尝试,但最终会出现不完整的列表或无限循环。任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

可视化您的数据

每当你发现自己遇到这样的问题时,就会想出一种简单的方法来想象发生了什么。为了了解您正在使用的图表,我编写了几行代码来可视化图表。

通过可视化,我注意到数据中可能存在一个小错误。我认为空格1和2应该连接到03而不是04。我在数据中对此进行了调整,并添加了额外的测试空间。

如果您愿意,可以通过展开下面的代码段来查看可视化效果。



const allSpaces=[{id:0,name:"Living Room",connectedSpaces:[1,2]},{id:1,name:"Hallway A",connectedSpaces:[0,3,4]},{id:2,name:"Hallway B",connectedSpaces:[0,3]},{id:3,name:"Bedroom",connectedSpaces:[1,2]}, {id:4,name:"Master bedroom",connectedSpaces:[1]}];

const Edge = (() => {
  // Do not support two way edges. Cache from and to internally:
  const cache = new Map();
  const Edge = (from, to) => {
    const id = `${Math.min(from, to)}.${Math.max(from, to)}`;
    const length = 1;
    
    if (!cache.has(id)) {
      cache.set(id, { from, to, id, length });
    }
    
    return cache.get(id);
  }
  
  return (from => to => Edge(from, to));
})();

const edges = uniques(allSpaces.reduce(
  (edges, node) => edges.concat(
    node.connectedSpaces.map(Edge(node.id))
  ), []
));

const Node = ({ id, name }) => ({ id, label: name });
const nodes = allSpaces.map(Node);

const network = new vis.Network(
  document.getElementById("cvs"), 
  { nodes, edges }, 
  {}
);

function uniques(arr) { return Array.from(new Set(arr).values()); }

<link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis-network.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.js"></script>
<div id="cvs" style="height: 300px"></div>
&#13;
&#13;
&#13;

能够查看我们的数据,可以更轻松地检查我们的功能是否有效!现在,您已经要求找到从A到B的所有路径。请注意,随着您添加更多节点,可能非常快速的路径数量增加。 (例如,查看Traveling Salesman Problem)。

最短路径?

如果你真的在寻找最短的路径,你可能需要调整下面的代码来使用Dijkstra's Shortest Path algorithm,就像SLaks在评论中建议的那样。

蛮力方法

但是,由于示例集很小,并且您要求所有路由,请强制它:

  • 定义一个空的路径集合
  • 定义起始节点
  • 将其ID添加到当前路径
  • 对于链接到的每个节点:
    • 检查它是否是我们的目的地;如果是这样:返回添加了当前路径的集合
    • 检查它是否已经在我们的路径中;如果是这样的话:跳过它(我们不想转圈)
    • 如果它不在我们的路径或目的地,请将其添加到路径并更深入

或者,在代码中:

const walk = (destination, paths, path, node) => {
  // Walking in circles
  if (path.includes(node.id)) // <-- expensive, for large paths use a Set
    return paths;

  // End reached
  if (node.id === destination)
    return paths.concat([path.concat(node.id)]);

  // Take next step recursively
  return node.connectedSpaces
    .reduce(
      (acc, id) => walk(destination, acc, path.concat(node.id), spaceMap.get(id)),
      paths
   );
}

这是一个正在运行的代码段,您可以使用它来逐步查看会发生什么:

&#13;
&#13;
const allSpaces=[{id:0,name:"Living Room",connectedSpaces:[1,2]},{id:1,name:"Hallway A",connectedSpaces:[0,3,4]},{id:2,name:"Hallway B",connectedSpaces:[0,3]},{id:3,name:"Bedroom",connectedSpaces:[1,2]}, {id:4,name:"Master bedroom",connectedSpaces:[1]}];

const spaceMap = new Map(allSpaces.map(s => [s.id, s]));

const walk = (destination, paths, path, node) => {
  // Walking in circles
  if (path.includes(node.id)) // <-- expensive, for large paths use a Set
    return paths;
  
  // End reached
  if (node.id === destination)
    return paths.concat([path.concat(node.id)]);
   
  // Take next step recursively
  return node.connectedSpaces
    .reduce(
      (acc, id) => walk(destination, acc, path.concat(node.id), spaceMap.get(id)),
      paths
   );
}

const calcRoute = (from, to) => {
  const routes = walk(to, [], [], spaceMap.get(from));
  
  return `
Found ${routes.length} route(s) to ${spaceMap.get(to).name}
${routes.map(r => r.map(id => spaceMap.get(id).name).join(" -> ")).join("\n")}
  `;
}

console.log(calcRoute(0, 3));
console.log(calcRoute(0, 4));
&#13;
&#13;
&#13;

相关问题