在二维数组中寻找最短路径 (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
我正在尝试实现一种算法,在以下二维数组中找到最短路径(从左上角到右下角):
[ [ '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