如何理解递归函数调用的返回值?
How to understand returning values from recursive function calls?
我正在尝试使用 Javascript 递归解决迷宫问题,如何从递归函数调用中 return 我的解决方案?
我正尝试在 Javascript 中使用递归创建迷宫求解器算法。我的迷宫将遵循以下模式:
let rawMaze =
[
[0, 1, 3],
[0, 1, 0],
[2, 1, 0]
],
哪里
0: wall
1: valid path
2: start
3: end
我从源数组创建一个对象,
let maze = []
constructMaze() {
for (let i = 0; i < 3; i++) {
maze[i] = [];
for (let j = 0; j < 3; j++) {
const Cell = {
x: j,
y: i,
state: rawMaze[i][j],
id: uniqueId()
};
this.maze[i].push(Cell);
}
}
console.table(this.maze);
}
我还使用辅助函数来获取任何给定单元格的邻居,
getNeighbours(x, y) {
let maze = this.maze;
let neighbours = [];
maze.forEach(row => {
row.forEach(cell => {
if (
(cell.x == x && cell.y == y + 1) ||
(cell.x == x && cell.y == y - 1) ||
(cell.y == y && cell.x == x + 1) ||
(cell.y == y && cell.x == x - 1)
) {
neighbours.push(cell);
}
});
});
return neighbours;
}
主要逻辑发生在我的 checkNeighbours 函数中,我在其中确定下一个可能的移动并跟进它们,
checkNeighbours(neighbours, path, visited) {
let validMoves = [];
neighbours.forEach(potentialMove => {
if (visited.indexOf(potentialMove.id) < 0) {
if (potentialMove.state !== 0) {
validMoves.push(potentialMove);
}
}
});
if (validMoves.length === 0) {
return;
} else {
let finish = validMoves.filter(cell => cell.state === 3);
console.log(finish);
if (finish.length === 1) {
return path;
}
}
validMoves.forEach(validMove => {
path.push(validMove);
visited.push(validMove.id);
this.checkNeighbours(
this.getNeighbours(validMove.x, validMove.y),
path,
visited
);
});
}
然后我继续尝试将所有这些放在一起并解决迷宫,
initSolve(maze) {
let maze = maze;
let start = [];
let paths = [];
let visited = [];
let current = null;
maze.forEach(row => {
row.forEach(cell => {
// Is start?
if ((start.length == 0) & (cell.state == 2)) {
start.push(cell);
visited.push(cell.id);
current = cell;
}
});
});
let result = this.checkNeighbours(
this.getNeighbours(current.x, current.y),
paths,
visited
);
console.log("test", result);
}
我的问题如下。使用这个非常人为和简单的迷宫配置,我已经逐步完成了代码并且可以确认我的
checkNeighbours()
函数会递归到达终点。此时,该函数有一个数组(变量 path),其中包含穿过迷宫的正确步骤。如果你愿意,我如何从递归调用中 return 这个 分支 ?当有多个 分支 时会发生什么?
我唯一能想到的就是使用全局变量,但我觉得这不太正确。
这是从 React 前端截取的,这是可运行的代码:
let rawMaze = [
[0, 1, 3],
[0, 1, 0],
[2, 1, 0]
]
let maze = []
function constructMaze() {
let counter = 0
for (let i = 0; i < 3; i++) {
maze[i] = [];
for (let j = 0; j < 3; j++) {
const Cell = {
x: j,
y: i,
state: rawMaze[i][j],
id: counter
};
maze[i].push(Cell);
counter++
}
}
}
function getNeighbours(x, y) {
let maze = this.maze;
let neighbours = [];
maze.forEach(row => {
row.forEach(cell => {
if (
(cell.x == x && cell.y == y + 1) ||
(cell.x == x && cell.y == y - 1) ||
(cell.y == y && cell.x == x + 1) ||
(cell.y == y && cell.x == x - 1)
) {
neighbours.push(cell);
}
});
});
return neighbours;
}
function checkNeighbours(neighbours, path, visited) {
let validMoves = [];
neighbours.forEach(potentialMove => {
if (visited.indexOf(potentialMove.id) < 0) {
if (potentialMove.state !== 0) {
validMoves.push(potentialMove);
}
}
});
if (validMoves.length === 0) {
return;
} else {
let finish = validMoves.filter(cell => cell.state === 3);
console.log(finish);
if (finish.length === 1) {
return path;
}
}
validMoves.forEach(validMove => {
path.push(validMove);
visited.push(validMove.id);
this.checkNeighbours(
this.getNeighbours(validMove.x, validMove.y),
path,
visited
);
});
}
function initSolve() {
let maze = constructMaze()
let start = [];
let paths = [];
let visited = [];
let current = null;
maze.forEach(row => {
row.forEach(cell => {
// Is start?
if ((start.length == 0) & (cell.state == 2)) {
start.push(cell);
visited.push(cell.id);
current = cell;
}
});
});
let result = this.checkNeighbours(
this.getNeighbours(current.x, current.y),
paths,
visited
);
console.log("test", result);
}
我建议再添加一个 class:
function Path() {
this.isValidPath = false;
this.pathArray = [];
}
还要将 checkNeighbours
函数修改为 rename/include 这些参数?
checkNeighbours(neighbours, paths, currentPathIndex, visited)
这样,paths
可以包含一个 Path
classes 的数组,并且您可以在找到有效路径时将 isValidPath
标志设置为 true (假设您还想在数组中包含无效和有效路径)。这将允许您 return 所有路径(分支)。每个分支都在 paths
数组中的 currentPathIndex
位置,一旦一条路径完成并且您想开始搜索另一条路径,您将在代码中递增它。
此外,目前 checkNeighbours
函数似乎对有效着法进行广度优先搜索。也许如果您将其重新设计为更多的深度优先遍历,那么您可以将每个有效路径(并排除任何无效路径)添加到您 return.
的路径数组中
我正在尝试使用 Javascript 递归解决迷宫问题,如何从递归函数调用中 return 我的解决方案?
我正尝试在 Javascript 中使用递归创建迷宫求解器算法。我的迷宫将遵循以下模式:
let rawMaze =
[
[0, 1, 3],
[0, 1, 0],
[2, 1, 0]
],
哪里
0: wall
1: valid path
2: start
3: end
我从源数组创建一个对象,
let maze = []
constructMaze() {
for (let i = 0; i < 3; i++) {
maze[i] = [];
for (let j = 0; j < 3; j++) {
const Cell = {
x: j,
y: i,
state: rawMaze[i][j],
id: uniqueId()
};
this.maze[i].push(Cell);
}
}
console.table(this.maze);
}
我还使用辅助函数来获取任何给定单元格的邻居,
getNeighbours(x, y) {
let maze = this.maze;
let neighbours = [];
maze.forEach(row => {
row.forEach(cell => {
if (
(cell.x == x && cell.y == y + 1) ||
(cell.x == x && cell.y == y - 1) ||
(cell.y == y && cell.x == x + 1) ||
(cell.y == y && cell.x == x - 1)
) {
neighbours.push(cell);
}
});
});
return neighbours;
}
主要逻辑发生在我的 checkNeighbours 函数中,我在其中确定下一个可能的移动并跟进它们,
checkNeighbours(neighbours, path, visited) {
let validMoves = [];
neighbours.forEach(potentialMove => {
if (visited.indexOf(potentialMove.id) < 0) {
if (potentialMove.state !== 0) {
validMoves.push(potentialMove);
}
}
});
if (validMoves.length === 0) {
return;
} else {
let finish = validMoves.filter(cell => cell.state === 3);
console.log(finish);
if (finish.length === 1) {
return path;
}
}
validMoves.forEach(validMove => {
path.push(validMove);
visited.push(validMove.id);
this.checkNeighbours(
this.getNeighbours(validMove.x, validMove.y),
path,
visited
);
});
}
然后我继续尝试将所有这些放在一起并解决迷宫,
initSolve(maze) {
let maze = maze;
let start = [];
let paths = [];
let visited = [];
let current = null;
maze.forEach(row => {
row.forEach(cell => {
// Is start?
if ((start.length == 0) & (cell.state == 2)) {
start.push(cell);
visited.push(cell.id);
current = cell;
}
});
});
let result = this.checkNeighbours(
this.getNeighbours(current.x, current.y),
paths,
visited
);
console.log("test", result);
}
我的问题如下。使用这个非常人为和简单的迷宫配置,我已经逐步完成了代码并且可以确认我的
checkNeighbours()
函数会递归到达终点。此时,该函数有一个数组(变量 path),其中包含穿过迷宫的正确步骤。如果你愿意,我如何从递归调用中 return 这个 分支 ?当有多个 分支 时会发生什么? 我唯一能想到的就是使用全局变量,但我觉得这不太正确。
这是从 React 前端截取的,这是可运行的代码:
let rawMaze = [
[0, 1, 3],
[0, 1, 0],
[2, 1, 0]
]
let maze = []
function constructMaze() {
let counter = 0
for (let i = 0; i < 3; i++) {
maze[i] = [];
for (let j = 0; j < 3; j++) {
const Cell = {
x: j,
y: i,
state: rawMaze[i][j],
id: counter
};
maze[i].push(Cell);
counter++
}
}
}
function getNeighbours(x, y) {
let maze = this.maze;
let neighbours = [];
maze.forEach(row => {
row.forEach(cell => {
if (
(cell.x == x && cell.y == y + 1) ||
(cell.x == x && cell.y == y - 1) ||
(cell.y == y && cell.x == x + 1) ||
(cell.y == y && cell.x == x - 1)
) {
neighbours.push(cell);
}
});
});
return neighbours;
}
function checkNeighbours(neighbours, path, visited) {
let validMoves = [];
neighbours.forEach(potentialMove => {
if (visited.indexOf(potentialMove.id) < 0) {
if (potentialMove.state !== 0) {
validMoves.push(potentialMove);
}
}
});
if (validMoves.length === 0) {
return;
} else {
let finish = validMoves.filter(cell => cell.state === 3);
console.log(finish);
if (finish.length === 1) {
return path;
}
}
validMoves.forEach(validMove => {
path.push(validMove);
visited.push(validMove.id);
this.checkNeighbours(
this.getNeighbours(validMove.x, validMove.y),
path,
visited
);
});
}
function initSolve() {
let maze = constructMaze()
let start = [];
let paths = [];
let visited = [];
let current = null;
maze.forEach(row => {
row.forEach(cell => {
// Is start?
if ((start.length == 0) & (cell.state == 2)) {
start.push(cell);
visited.push(cell.id);
current = cell;
}
});
});
let result = this.checkNeighbours(
this.getNeighbours(current.x, current.y),
paths,
visited
);
console.log("test", result);
}
我建议再添加一个 class:
function Path() {
this.isValidPath = false;
this.pathArray = [];
}
还要将 checkNeighbours
函数修改为 rename/include 这些参数?
checkNeighbours(neighbours, paths, currentPathIndex, visited)
这样,paths
可以包含一个 Path
classes 的数组,并且您可以在找到有效路径时将 isValidPath
标志设置为 true (假设您还想在数组中包含无效和有效路径)。这将允许您 return 所有路径(分支)。每个分支都在 paths
数组中的 currentPathIndex
位置,一旦一条路径完成并且您想开始搜索另一条路径,您将在代码中递增它。
此外,目前 checkNeighbours
函数似乎对有效着法进行广度优先搜索。也许如果您将其重新设计为更多的深度优先遍历,那么您可以将每个有效路径(并排除任何无效路径)添加到您 return.