假设我有下面的迷宫:(格式不正确)
#########################################
S... #... # # #... #
###.# ##### #.#.### # ### # ###.#.# #####
#...# #...# #.#...# # # #.#.# #...#
#.#####.#.###.###.##### ##### #.#.###.#.#
#.....#.#..... #...# #.#.....#.#
# ###.#.####### ###.###########.#######.#
# #.#.# # #...#......... # #.#
### #.#.# ### #######.#.########### # #.#
# # #.#.# # # # #...# # # .#
# # #.#.# # ### # # ##### ### # #######.#
# #...# # # # # .E
#########################################
S 表示迷宫的起点,E 表示迷宫的终点。我有两个给定的课程;迷宫
和细胞
.我必须构建以下递归助手方法来找到迷宫的解决方案:
-findPath(currentMaze:Maze, current:Cell, path:ArrayList<Cell>):ArrayList<Cell
此方法递归地找到一条从当前迷宫的开始到结束的路径,该路径通过当前Cell。该路径是从迷宫的开始到当前单元格的单元格序列的ArrayList(即到目前为止探索的路径)。为了避免超过所需的路径,算法应避免重新访问已在此路径中的单元格。如果没有从当前到结束的路径最多只通过每个Cell一次,则算法应返回null。否则,它应返回从迷宫开始到结束的完整路径,作为ArrayList中的单元格序列。您必须将其实现为递归算法。为了探索尚未访问的邻居的所有路径,您需要使用Maze的getNeighbors。
为了构建这个递归方法,我给出了以下方法:
+getStartCell():Cell Returns the start Cell of the maze
+getEndCell():Cell Returns the end Cell of the maze
+getNeighbors(currentCell:Cell):
ArrayList<Cell>
Returns a list of all the cells that are connected to
currentCell. If there is a wall between
currentCell and its neighbor, it is not added to this
collection.
到目前为止,我是这样做的:
private static ArrayList <Cell> findPath(Maze currentMaze,Cell current,ArrayList <Cell> path){
// Base Case
if (current == currentMaze.getEndCell()) {
return path;
}
if(currentMaze.getNeighbors(current).size()!=0)
currentMaze.getStartCell();
currentMaze.getNeighbors(current);
currentMaze.getEndCell();
}
}
我真的在努力构建这种方法。
问问你自己,你怎样才能找到这条路。在每一步考虑你到达一些细胞。
像这样的东西:
private static ArrayList <Cell> findPath(Maze currentMaze,Cell current,ArrayList <Cell> currentPath, ArrayList< ArrayList <Cell> > solutionsFound){
// step 1
if (currentPath.exists(current)) {
return;
}
// step 2
currentPath.add(current);
// step 3
if(current == currentMaze.getStartCell()){
solutionsFound.add(currentPath.clone());
currentPath.remove(current);
return;
}
// step 4
ArrayList<Cell> neighbors = currentMaze.getNeighbors(current);
for(int i=0;i<neighbors.size();i++){
findPath(currentMaze, neighbors[i], currentPath, solutionsFound);
}
// step 5
currentPath.remove(current);
}
开头为 :
ArrayList< ArrayList <Cell> > solutionsFound = new ArrayList< ArrayList <Cell> >();
ArrayList <Cell> currentPath= new ArrayList <Cell> ();
findPath(currentMaze, currentMaze.getStartCell(), new ArrayList <Cell>, solutionsFound);
最后,solutionsFound
包含解决方案,currentPath 应为空。
好的,这是。你不仅需要 DFS,还需要一种存储找到的路径的方法。
您为 findPath
建议的方法签名将不起作用。它的 path
参数是一个列表,它将在遍历时存储所有节点,因为即使它是一个递归算法,我们也不会在将列表传递给下一个 findPath
调用之前完全复制列表,坦率地说,我们不应该这样做来提高性能并减少内存消耗。
我能想到的最简单的方法是让每个细胞都指向它的父细胞。父细胞是被发现为邻居的细胞。
我们必须使用以下签名来查找路径
List<Cell> findPath(Maze currentMaze, Cell current)
当我们到达结束节点时,我们需要返回所有递归
,因此状态必须存储在findPath
之外。
Rest很简单,我们可以使用以下算法(这是伪代码)
path = null
findPath(maze, startCell)
printPath(maze, path)
findPath(currentMaze, current)
if curent = endCell
list = []
while(current != null)
list.add(0, current)
current = current.parent
path = list
else if path != null
current.visitStatus = IN_PROGRESS
neighbours = getUnVisitedNeighbours(current)
for each neibhbour in neighbours
neighbour.parent = current
findPath(currentMaze, neighbour)
current.visitStatus = VISITED
printPath(currentMaze, path)
for each cell in path
cell.ch = 'O' //since path references are same as maze it will update maze as well
print maze
注意:这个算法不产生最短路径,只要它能找到任何路径,它就返回。
这是一个实际的Java实现。它从文本文件中读取迷宫。
下面是带有示例迷宫文本文件的Github链接。
https://github.com/ConsciousObserver/stackoverflow/tree/master/TestMaze
package com.example;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.stream.Stream;
import com.example.TestMaze.Cell.VisitStatus;
public class TestMaze {
static List<Cell> resultPath = null;
public static void main(String[] args) {
String filePath = "maze2.txt";
Maze currentMaze = new Maze(filePath);
findPath(currentMaze, currentMaze.startCell);
if(resultPath == null) {
System.out.println("\nNo path exists for the Maze");
} else {
System.out.println("\nPath size : " + resultPath.size());
printPathOnMaze(currentMaze, resultPath);
}
}
private static void printPathOnMaze(Maze maze, List<Cell> path) {
path.stream()
.filter(cell-> !maze.isStartCell(cell) && !maze.isEndCell(cell))
.forEach(cell-> cell.setCh('O'));
maze.printCells();
}
private static List<Cell> findPath(Maze currentMaze, Cell current) {
if(currentMaze.isEndCell(current)) {
resultPath = new ArrayList<>();
Cell traversalCell = current;
while(traversalCell != null) {
resultPath.add(0, traversalCell);
traversalCell = traversalCell.getParentCell();
}
return resultPath;
}
if(resultPath == null) {
if(Maze.isWall(current)) {
current.setVisitStatus(VisitStatus.VISITED);
} else {
current.setVisitStatus(VisitStatus.IN_PROGRESS);
List<Cell> neighbourList = currentMaze.getNeighbours(current);
neighbourList.stream()
.filter(cell -> cell.getVisitStatus() == VisitStatus.UNVISITED)
.filter(cell -> cell.getVisitStatus() == VisitStatus.UNVISITED)
.forEach(neighbour -> {
neighbour.setParentCell(current);
findPath(currentMaze, neighbour);
});
current.setVisitStatus(VisitStatus.VISITED);
}
}
return null;
}
public static boolean isCellInPath(Cell cell, List<Cell> path) {
return path.stream().anyMatch(c -> c.getI() == cell.getI() && c.getJ() == c.getJ());
}
public static class Cell {
private int i, j;
private char ch;
private Cell parentCell;
public enum VisitStatus {VISITED, IN_PROGRESS, UNVISITED};
private VisitStatus visitStatus = VisitStatus.UNVISITED;
public Cell(int i, int j, char ch) {
super();
this.i = i;
this.j = j;
this.ch = ch;
}
public int getI() {
return i;
}
public int getJ() {
return j;
}
public char getCh() {
return ch;
}
public void setCh(char ch) {
this.ch = ch;
}
public VisitStatus getVisitStatus() {
return visitStatus;
}
public void setVisitStatus(VisitStatus visitStatus) {
this.visitStatus = visitStatus;
}
public Cell getParentCell() {
return parentCell;
}
public void setParentCell(Cell parentCell) {
this.parentCell = parentCell;
}
}
public static class Maze {
private Cell[][] grid;
private Cell startCell;
private Cell endCell;
private static final char START_CELL_CHAR = 'S';
private static final char END_CELL_CHAR = 'E';
private static final char WALL_CHAR = '#';
private static final char EMPTY_SPACE_CHAR = '.';
public Maze(String filePath) {
grid = createFromFile(filePath);
printCells();
}
public Cell[][] getGrid() {
return grid;
}
public Cell getStartCell() {
return startCell;
}
public Cell getEndCell() {
return endCell;
}
public boolean isStartCell(Cell cell) {
return startCell.getI() == cell.getI() && startCell.getJ() == cell.getJ();
}
public boolean isEndCell(Cell cell) {
return endCell.getI() == cell.getI() && endCell.getJ() == cell.getJ();
}
List<Cell> getNeighbours(Cell cell) {
List<Cell> neighboursList = new ArrayList<>();
int mazeHeight = grid.length;
int mazeWidth = grid[0].length;
if(cell.getI() - 1 > 0) {
neighboursList.add(grid[cell.getI() - 1][cell.getJ()]);
}
if(cell.getI() + 1 < mazeHeight) {
neighboursList.add(grid[cell.getI() + 1][cell.getJ()]);
}
if(cell.getJ() - 1 > 0) {
neighboursList.add(grid[cell.getI()][cell.getJ() - 1]);
}
if(cell.getJ() + 1 < mazeWidth) {
neighboursList.add(grid[cell.getI()][cell.getJ() + 1]);
}
return neighboursList;
}
public static boolean isWall(Cell cell) {
return cell.getCh() == WALL_CHAR;
}
public static boolean isEmptySpace(Cell cell) {
return cell.getCh() == EMPTY_SPACE_CHAR;
}
public void printCells() {
Stream.of(grid).forEach(row-> {
Stream.of(row).forEach(cell -> System.out.print(cell.getCh()) );
System.out.println();
});
}
private Cell[][] createFromFile(String filePath) {
Cell[][] maze = null;
try(Scanner scan = new Scanner(Paths.get(filePath)) ) {
List<Cell[]> list = new ArrayList<>();
for(int i = 0; scan.hasNext(); i++) {
String line = scan.nextLine();
char[] chArr = line.toCharArray();
Cell[] row = new Cell[chArr.length];
for(int j = 0; j < chArr.length; j++) {
char ch = chArr[j];
Cell cell = new Cell(i, j, ch);
row[j] = cell;
if(ch == START_CELL_CHAR) {
startCell = cell;
} else if (ch == END_CELL_CHAR) {
endCell = cell;
}
}
list.add(row);
}
if(startCell == null || endCell == null) {
throw new RuntimeException("Start cell or End cell not present");
}
maze = list.toArray(new Cell[][]{});
} catch(Exception ex) {
ex.printStackTrace();
}
return maze;
}
}
}
注意:您的样品没有解决方案。
具有解决方案的样本输入
#########################################
S....#....#.#.#....#.........#..........E
###.#.#####.#.#.###.#.#.#.#.###.#.#.#####
#...#.#...#.#.#...#.#.#.#.#.#.#...#......
#.#####.#.###.###.#####..####.#.#.###.#.#
#.....#.#......#...#.#.#.....#.#.........
#.###.#.#######.###.########.##.#######.#
#.#.#.#.#.#...#..........#.#.#...........
###.#.#.#.###.#######.#.####.######.#.#.#
#.#.#.#.#.#.#.#.#...#.#.#..#.............
#.#.#.#.#.#.###.#.#.#####.###.#.#######.#
#.#.....................................#
#########################################
输出
Path size : 89
#########################################
SOOO.#....#.#.#....#.........#...OOOOOOOE
###O#.#####.#.#.###.#.#.#.#.###.#O#.#####
#OOO#.#...#.#.#...#.#.#.#.#.#.#..O#..OOO.
#O#####.#.###.###.#####..####.#.#O###O#O#
#OOOOO#.#......#...#.#.#.....#.#.OOOOO.O.
#.###O#.#######.###.########.##.#######O#
#.#.#O#.#.#...#..........#.#.#.........O.
###.#O#.#.###.#######.#.####.######.#.#O#
#.#.#O#.#.#.#.#.#OOO#.#.#..#.OOO.......O.
#.#.#O#.#.#.###.#O#O#####.###O#O#######O#
#.#..OOOOOOOOOOOOO.OOOOOOOOOOO.OOOOOOOOO#
#########################################
注意:广度优先搜索可能会给出更好的结果。
在这篇文章中,biziclop为非递归深度优先搜索算法插入了伪代码。 如果我们想使用递归DFS算法来检查节点的适当性,我们可以利用两个变体:pre-order(当一个节点在其子节点之前检查时)和post-order(当子节点在节点之前检查时),加上仅针对二叉树的第三个变体(顺序:左子树,然后节点,然后右子树)。 如果可能的话,我对这三个变体都很感兴趣,所以我试图修改biziclop的伪代码,以便获
主要内容:深度优先搜索(简称“深搜”或DFS),广度优先搜索,总结前边介绍了有关图的 4 种存储方式,本节介绍如何对存储的图中的顶点进行遍历。常用的遍历方式有两种: 深度优先搜索和 广度优先搜索。 深度优先搜索(简称“深搜”或DFS) 图 1 无向图 深度优先搜索的过程类似于树的先序遍历,首先从例子中体会深度优先搜索。例如图 1 是一个无向图,采用深度优先算法遍历这个图的过程为: 首先任意找一个未被遍历过的顶点,例如从 V1 开始,由于 V1 率先访问过了,所以
本文向大家介绍深度优先搜索,包括了深度优先搜索的使用技巧和注意事项,需要的朋友参考一下 图遍历是按某种系统顺序访问图的所有顶点的问题。遍历图主要有两种方法。 广度优先搜索 深度优先搜索 深度优先搜索(DFS)算法从顶点v开始,然后遍历到之前未访问过的相邻顶点(例如x),并将其标记为“已访问”,然后继续处理x的相邻顶点,依此类推。 如果在任何一个顶点上遇到所有相邻顶点都被访问过,则它将回溯直到找到具
3. 深度优先搜索 现在我们用堆栈解决一个有意思的问题,定义一个二维数组: int maze[5][5] = { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; 它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的路线
有没有人看到上述算法存在缺陷?我不是图论方面的专家,但我认为我对递归和迭代有很好的把握,我相信这也是一样的。我想让它在功能上与递归算法等价。它应该维护第一个算法的所有属性,即使不需要这些属性。 当我开始写它的时候,我并不认为我会有三个循环,但结果就是这样。当我环顾谷歌时,我看到了其他的迭代算法,它们只有一个双重嵌套的循环,然而,它们似乎不是从多个来源出发的。(即他们不会发现不连通图的每一个顶点)