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

Finding shortest path in two dimensional array (Javascript)

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

      [ [ '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 实现?

解决问题的一种方法是首先将二维数组表示为图形,其中每个字母都是一个节点,如果两个节点代表的字母在数组中相邻,则两个节点之间存在一条边,这些字母不同(一个 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 算法是开始使用最短路径算法的最佳方法,它使用最广泛,并且在您将遇到的大多数情况下都足够快。

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

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

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

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

只有在以下情况下才能转到下一个节点: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]);

Does anyone know of a simple Graph implementation that can help me solve this problem?

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

您可以 further optimise 将其用于具有双向搜索和目标指标 (A*) 的用例。

因为它表示一个 undirected unweighted graph,你可以简单地使用 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