【问题标题】:Finding shortest path in two dimensional array (Javascript)在二维数组中查找最短路径(Javascript)
【发布时间】:2019-03-19 10:59:12
【问题描述】:

我正在尝试实现一种算法,该算法在以下二维数组中找到最短路径(从左上角到右下角):

      [ [ 'A', 'A', 'A', 'B', 'A' ],
        [ 'B', 'B', 'B', 'B', 'B' ],
        [ 'A', 'B', 'A', 'A', 'A' ],
        [ 'A', 'B', 'B', 'B', 'B' ],
        [ 'A', 'A', 'A', 'A', 'A' ] ]

规则是,路径必须在 A 和 B 之间交替。

输出必须是一个数字,指定遍历数组所需的最小步数。 (在示例中,预期输出为 13)

有人知道可以帮助我解决这个问题的简单 Graph 实现吗?

【问题讨论】:

  • 往返的最短路径?
  • 数组中的每个值是什么?您能否从所提供的数据中提供您期望的示例输出。
  • 您能否为您的问题陈述提供更多意见
  • 对不起,是从左上角到右下角
  • 我已经更新了问题,如果您需要更多信息,请告诉我

标签: javascript algorithm graph-theory


【解决方案1】:

由于它表示一个无向 未加权图,你可以简单地使用BFS:

const m =
  [ [ 'A', 'A', 'A', 'B', 'A' ],
    [ 'B', 'B', 'B', 'B', 'B' ],
    [ 'A', 'B', 'A', 'A', 'A' ],
    [ 'A', 'B', 'B', 'B', 'B' ],
    [ 'A', 'A', 'A', 'A', 'A' ] ]

let successors = (root, m) => {
  let connectedCells = [
    [root[0] - 1, root[1]],
    [root[0], root[1] - 1],
    [root[0] + 1, root[1]],
    [root[0], root[1] + 1]
  ]

  const validCells = connectedCells.filter(
    (cell) => (
      cell[0] >= 0 && cell[0] < m.length 
      && cell[1] >= 0 && cell[1] < m[0].length)
  )

  const successors = validCells.filter(
    (cell) => (m[cell[0]][cell[1]] !== m[root[0]][root[1]])
  )

  return successors
}

const buildPath = (traversalTree, to) => {
  let path = [to]
  let parent = traversalTree[to]
  while (parent) {
    path.push(parent)
    parent = traversalTree[parent]
  }
  return path.reverse()
}

const bfs = (from, to) => {
  let traversalTree = []
  let visited = new Set
  let queue = []
  queue.push(from)

  while (queue.length) {
    let subtreeRoot = queue.shift()
    visited.add(subtreeRoot.toString())

    if (subtreeRoot.toString() == to.toString()) return buildPath(traversalTree, to)

    for (child of successors(subtreeRoot, m)) {
      if (!visited.has(child.toString())){
        traversalTree[child] = subtreeRoot
        queue.push(child)
      }
    }
  }
}


console.log(bfs([0,0], [4,4]).length) // => 13

【讨论】:

    【解决方案2】:

    您可以将网格用作图形,而无需将它们转换为通常的图形邻接表表示。

    所以每一对(行、列)都是一个节点,

    只有在以下情况下才能转到下一个节点:2 个节点是邻居并且具有不同的值,

    邻接表的目的是提高相邻节点的效率,但使用网格单元,您可以始终检查所有 4 个方向并处理存在的节点。

    示例代码:

    let A = [ [ 'A', 'A', 'A', 'B', 'A' ],
            [ 'B', 'B', 'B', 'B', 'B' ],
            [ 'A', 'B', 'A', 'A', 'A' ],
            [ 'A', 'B', 'B', 'B', 'B' ],
            [ 'A', 'A', 'A', 'A', 'A' ] ];
    		
    let visited = new Set();
    let rows = A.length;
    let columns = A[0].length;
    let distance = Array(rows).fill().map(() => Array(columns).fill(-1));
    distance[0][0]=0;
    let Q = []; //Queue
    Q.push([0,0]);
    visited.add([0,0].toString());
    
    let dr = [-1,1,0,0]; 
    let dc = [0,0,-1,1]; 
    
    while(Q.length > 0)
    {
    	let cur = Q.shift();
    	let row = cur[0];
    	let col = cur[1];
    	
    	for(let k=0; k<4; k++)
    	{
    		let newRow = row + dr[k];
    		let newCol = col + dc[k];
    		
    		if(!visited.has([newRow,newCol].toString()) && newRow>=0 && newCol >=0 && newRow < rows && newCol < columns && A[newRow][newCol] !== A[row][col])
    		{
    			visited.add([newRow,newCol].toString());
    			distance[newRow][newCol] = distance[row][col] + 1;
    			Q.push([newRow,newCol]);
    		}
    	}
    }
    
    if(distance[rows-1][columns-1] === -1)console.log("Path does not exist");
    else console.log(distance[rows-1][columns-1]);

    【讨论】:

      【解决方案3】:

      解决您的问题的一种方法是首先将您的二维数组表示为一个图,其中每个字母是一个节点,如果两个节点代表的字母是数组中的邻居,则两个节点之间存在一条边,并且这些字母是不同(一个A 和一个B)。
      然后,您所要做的就是使用经典的最短路径算法(例如 Dijkstra 或 A*)来找到图的两个节点之间的最短路径。这相当于找到数组的两个字母之间的最短路径。

      编辑: 这是一个伪代码来回答您在评论中提出的问题。

      nodes = init_a_2d_array_of_graph_nodes(ARRAY_WIDTH, ARRAY_HEIGHT)
      for i from 1 to ARRAY_WIDTH:
          for j from 1 to ARRAY_HEIGHT:
              if i < ARRAY_WIDTH and array[i][j] != array[i+1][j]:
                  add_edge(nodes[i][j], nodes[i+1][j])
              if j < ARRAY_HEIGHT and array[i][j] != array[i][j+1]:
                  add_edge(nodes[i][j], nodes[i][j+1])
      

      首先,您需要初始化一个图结构。如果你不知道怎么做,上网查一下,一定有很多方法可以做到,很简单。

      然后,您需要为数组中的每个字母创建一个节点。将这些节点存储在二维数组中也很方便,因此您可以轻松找出数组的哪个字母对应于图形中的哪个节点。 然后,对于所有相邻字母,您检查这些字母是否不同(即在 2 if 条件中检查的内容)。如果是这种情况,则将两个节点与一条边连接起来。

      之后,您需要在源节点和目标节点之间的图形上运行最短路径算法。 Dijkstra 算法是开始使用最短路径算法的最佳方式,它使用最广泛,并且在您将遇到的大多数情况下都足够快。

      最后,一旦有了路径,您需要检索图形节点的索引(行和列),这将为您提供字母数组中的相应路径。

      如果还有不明白的地方欢迎再次评论。

      【讨论】:

      • 这正是我现在想要做的。我现在遇到的问题是找到一种从数组创建邻接列表的方法。
      【解决方案4】:

      有人知道可以帮助我解决这个问题的简单 Graph 实现吗?

      Dijkstra algortihm 用于查找两个节点之间的最短路径。二维数组中的每个位置都代表一个节点,边缘是从满足“交替”规则的周围节点动态派生的。

      您可以further optimise 使用双向搜索和目标指标 (A*)。

      【讨论】:

      • IMO 将 BFS 用于无向非加权图会更简单(并且在任何情况下 - 渐近更快)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-07
      • 2020-09-19
      相关资源
      最近更新 更多