【问题标题】:How to understand returning values from recursive function calls?如何理解递归函数调用的返回值?
【发布时间】:2019-04-04 20:37:13
【问题描述】:

我正在尝试使用 Javascript 递归解决迷宫问题,如何从递归函数调用中返回我的解决方案?

我正在尝试在 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),其中包含通过迷宫的正确步骤。如果您愿意,我如何从递归调用中返回这个分支?当有多个分支时会发生什么? 我唯一能想到的就是使用全局变量,但我觉得这不正确。

这是从 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);
        }

【问题讨论】:

  • 可以添加可运行代码吗?
  • 好点!谢谢!我添加了可运行代码
  • "当有多个分支时会发生什么?" - 你需要决定。当实际上有多个值时,简单地returning 一个值并不能很好地工作。那么你期望的最终结果是什么?可能是一组分支?
  • 是的,我正在寻找一组分支。函数执行后,我会循环遍历数组并选择最短的一个。这只能通过更顶级的变量来完成吗?

标签: javascript recursion maze


【解决方案1】:

我可以建议添加另一个类:

function Path() {
    this.isValidPath = false;
    this.pathArray = [];
}

还要重新设计checkNeighbours 函数以重命名/包含这些参数?

checkNeighbours(neighbours, paths, currentPathIndex, visited)

这样,paths 可以包含 Path 类的数组,并且您可以在找到有效路径时将 isValidPath 标志设置为 true(假设您还想在数组中包含无效和有效路径)。这将允许您返回所有路径(分支)。每个分支都将位于 currentPathIndex 位置的 paths 数组中,一旦一条路径完成并且您想开始搜索另一条路径,您将在代码中递增。

此外,目前checkNeighbours 函数似乎对有效移动进行广度优先搜索。也许如果您将其改造成更多的深度优先遍历,那么您可以将每个有效路径(并排除任何无效路径)添加到您返回的路径数组中。

【讨论】:

  • 感谢您的详尽回答。至于是什么类型的搜索,你告诉我!我并没有真正选择一种算法,我只是想着如何天真地解决这个问题,然后实现它。我会尝试添加课程。目前,我不明白为什么我需要 currentPathIndex。
  • 对于currentPathIndex,我在想path.push(validMove); 必须改为paths[currentPathIndex].pathArray.push(validMove);。然后你会为下一条路径增加它。但这只是一种方法。我相信你可以用另一种方式来做,而不需要这个变量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-16
  • 2020-01-24
  • 2013-09-29
  • 1970-01-01
  • 2021-07-09
  • 2020-10-10
相关资源
最近更新 更多