在二维数组中查找最短路径(Javascript)

时间:2019-03-19 10:59:13

标签: javascript algorithm graph-theory

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

      [ [ '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实现可以帮助我解决这个问题吗?

4 个答案:

答案 0 :(得分:2)

解决问题的一种方法是首先将2D数组表示为图,其中每个字母是一个节点,并且如果两个节点之间的字母在数组中是相邻的,则两个节点之间存在一条边。不同(一个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])

首先,您需要初始化图结构。如果您不知道该怎么做,请在线检查,必须有很多方法可以做到,这很简单。

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

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

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

如果仍有您不理解的地方,请随时发表评论。

答案 1 :(得分:2)

好吧,您可以将网格用作图形,而无需将其转换为常用的图形邻接表表示形式。

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

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

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

示例代码:

entityManager.createQuery(...)

答案 2 :(得分:0)

  

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

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

您可以通过双向搜索和目标指标(A *)为您的用例further optimise

答案 3 :(得分:0)

由于它表示无向 未加权图,因此您可以简单地使用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