小白学数据结构(1)——java实现简单迷宫算法

思想

将迷宫抽象成具有2种或多种值的row 行 column 列的网格,状态’1’ 代表墙体,状态’0’代表可通行。增加一个状态‘*’代表可能的最终路径。

由于网上没有合适的图片,自己用excel画了简易的图便于理解。
其中蓝色的坐标表示起点,绿色的坐标表示终点。
在这里插入图片描述
在这里插入图片描述
难点是对每一个坐标,判断周边(上下左右)是否有可行的cell?以及如何判断crack是否成功,如果没有成功怎么办?

二、java代码实现

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;

/**
 * @apiNote
 * 迷宫问题
 *
 * 规则:
 *      '1'代表墙体,'0'代表可通行
 *      不能"穿墙"
 *
 * @author leejack
 */
@SuppressWarnings("all")
public class Maze {

    /**
     * program starter
     *
     * @param args
     */
    public static void main(String[] args){
        //1 initialize the maze
        char[][] chars = initialize();

        //2 print the maze
        printMaze(chars);

        //3 regular expression verifacation
        Scanner sc = new Scanner(System.in);
        String regex = "\\d*";

        String rowStr = null;
        do {
            System.out.println("请再次输入迷宫的行数");
            rowStr = sc.next();
        }while(!rowStr.matches(regex));
        int row = Integer.parseInt(rowStr);

        String columnStr = null;
        do {
            System.out.println("请再次输入迷宫的列数");
            columnStr = sc.next();
        }while(!columnStr.matches(regex));
        int column = Integer.parseInt(columnStr);

        String beginXStr = null;
        String beginYStr = null;
        do {
            System.out.println("请输入起点坐标,前一个是x轴坐标,后一个是y轴坐标,按回车确认");
            beginXStr = sc.next();
            beginYStr = sc.next();
        }while(!beginXStr.matches(regex) && !beginYStr.matches(regex));
        int beginX = Integer.parseInt(beginXStr);
        int beginY = Integer.parseInt(beginYStr);

        String endXStr = null;
        String endYStr = null;
        do {
            System.out.println("请输入终点坐标,前一个是x轴坐标,后一个是y轴坐标,按回车确认");
            endXStr = sc.next();
            endYStr = sc.next();
        }while(!endXStr.matches(regex) && !endYStr.matches(regex));
        int endX = Integer.parseInt(endXStr);
        int endY = Integer.parseInt(endYStr);

        //4 initialize object
        System.out.println("生成迷宫中");
        Cell[][] maze = MazeCracker.createMaze(chars, row, column);
        MazeCracker cracker = new MazeCracker(maze, beginX, beginY, endX, endY, row, column);

        //5 crack the maze
        System.out.println("破解迷宫中");
        cracker.crackMaze();

        //6 print the result
        cracker.printResult(cracker.result);
    }

    /**
     * initialize the character array and print the initial prompt message
     *
     * @return character array
     */
    public static char[][] initialize(){
        System.out.println("欢迎使用迷宫破解系统");
        System.out.println("请输入迷宫的行数");
        Scanner scanner = new Scanner(System.in);
        int row = Integer.parseInt(scanner.next());
        System.out.println("请输入迷宫的列数");
        int column = Integer.parseInt(scanner.next());
        System.out.println("迷宫的行数为:" + row + "迷宫的列数为:" + column);
        System.out.println("接下来请输入迷宫中的每一个单元格");
        System.out.println("'0'代表可通行,‘1'代表是墙体,不可通行\r");

        String elements;
        char[][] chars = new char[row][column];
        for (int x = 0; x < row;){
            System.out.println("请输入第" + (x + 1) + "行,共" + column + "个元素,中间不要有空格");
            elements = scanner.next();

            //check the validity of the input
            if (elements.length() != column){
                System.out.println("您输入的元素不足或者超出了字符数组的容量,请重新输入");
                continue;
            }

            char[] rows = elements.toCharArray();
            for (int y = 0; y < rows.length; y++){
                chars[x][y] = rows[y];
            }
            x++;
        }

        System.out.println("输入完毕,打印迷宫中");

        return chars;
    }

    /**
     * print the input result
     *
     * @param chars
     */
    public static void printMaze(char[][] chars) {
        for (char[] rows : chars) {
            for (char ch : rows) {
                System.out.print(ch + "  ");
            }
            System.out.println();
        }

        System.out.println("打印迷宫完毕");
    }

}

/**
 * 单元格
 */
class Cell{
    int x;//x coordinate
    int y;//y coordinate
    char mark;
    boolean visited;

    public Cell(int x, int y, char mark, boolean visited) {
        this.x = x;
        this.y = y;
        this.mark = mark;
        this.visited = visited;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Cell){
            Cell cell = (Cell) obj;
            return this.x == cell.x && this.y == cell.y;
        }else{
            return super.equals(obj);
        }
    }


}

class MazeCracker{
    Cell[][] cells;
    int beginX;//起点x坐标
    int beginY;//起点y坐标
    int endX;//终点x坐标
    int endY;//终点y坐标
    int row;
    int column;
    boolean cracked = false;
    Cell[][] result = {};

    public MazeCracker(Cell[][] cells, int beginX, int beginY, int endX, int endY, int row, int column) {
        this.cells = cells;
        this.beginX = beginX;
        this.beginY = beginY;
        this.endX = endX;
        this.endY = endY;
        this.row = row;
        this.column = column;
    }

    /**
     * make char[][] change into Cell[][]
     *
     * @param chars 字符数组
     * @param row 行
     * @param column 列
     * @return 单元格的二维数组
     */
    public static Cell[][] createMaze(char[][] chars, int row, int column) {
        if (checkCharArray(chars, row)) {
            int x = 0;
            int y = 0;
            Cell[][] cells = new Cell[row][column];
            for (char[] rows : chars) {
                if (rows.length != row) {
                    throw new IllegalStateException("Incorrect length in the binary array!");
                }

                for (char ch : rows) {
                    Cell cell;
                    //1 表示墙体
                    if (ch == '1') {
                        cell = new Cell(x, y, '1', false);
                    } else {
                        cell = new Cell(x, y, '0', false);
                    }

                    cells[x][y] = cell;
                    y++;
                    if (y == column) {
                        y = 0;
                    }
                }
                x++;
                if (x == row) {
                    break;
                }
            }
            return cells;
        }else{
            throw new IllegalStateException("Illegal character array");
        }
    }

    /**
     * check whether the character array is legal simply
     *
     * @param chars the input character array
     * @param row row of input
     * @return legal
     */
    public static boolean checkCharArray(char[][] chars, int row) {
        return chars.length == row;
    }

    /**
     * crack the maze
     *
     */
    public void crackMaze() {
        //initialized the member variables
        cracked = false;

        //check the start point position and the end point position legalities
        if (isPositionLegal(beginX, beginY) && isPositionLegal(endX, endY)) {
            int count = 0;

            //set the start point position and the end point position
            Cell begin = new Cell(beginX, beginY, '0', true);
            Cell end = new Cell(endX, endY, '0', true);

            Cell[][] cells = this.cells;

            //check all possible path
            List<Cell> paths;
            paths = getAllPossiblePaths(begin);

            if (paths.size() == 0){
                System.out.println("起点坐标为死路,这个迷宫没有解法");
            }else{

                Cell newCell;
                Stack<Cell> stack = new Stack<>();
                stack.push(begin);

                loop1: while(!stack.isEmpty()){
                    loop2: while(true){
                        //avoid endless loops
                        if (paths.size() == 0){
                            newCell = stack.pop();
                            paths = getAllPossiblePaths(newCell);
                        }

                        for (Cell path : paths) {
                            path.visited = true;
                            path.mark = '*';
                            newCell = path;
                            count++;
                            System.out.println("第" + count + "次尝试,坐标为:[" + newCell.x + ", " + newCell.y + "]");
                            stack.push(newCell);

                            if (newCell.equals(end)) {
                                cracked = true;
                                break loop1;
                            } else {
                                paths = getAllPossiblePaths(path);
                                if (paths.size() == 0){
                                    //if there is impossible path,pop the element from the stack and try again
                                    //如果没有可能的路径了,则从栈中弹出栈顶元素重新尝试
                                    newCell.mark = '0';
                                    newCell = stack.pop();
                                    paths = getAllPossiblePaths(newCell);
                                }else{
                                    break loop2;
                                }

                            }
                        }
                    }
                }

            }

            //check whether the crack is successful
            if (cracked){
                //successful
                System.out.println("破解迷宫成功");
                result = cells;
            }else{
                //fail
                System.out.println("这个迷宫没有解法");
            }
        }
    }

    /**
     * get all possible paths
     *
     * @param cell 单元格
     * @return 可能的路径
     */
    public List<Cell> getAllPossiblePaths(Cell cell) {
        int x = cell.x;
        int y = cell.y;
        ArrayList<Cell> list = new ArrayList<>();

        //up
        //determine whether cell of upward direction is legal
        if (x != row){
            Cell u_cell = cells[x + 1][y];
            if (!u_cell.visited && u_cell.mark == '0'){
                list.add(u_cell);
            }
        }

        //down
        //determine whether cell of downward direction is legal
        if (x != 0){
            Cell d_cell = cells[x - 1][y];
            if (!d_cell.visited && d_cell.mark == '0'){
                list.add(d_cell);
            }
        }

        //left
        //determine whether cell of leftward direction is legal
        if (y != 0){
            Cell l_cell = cells[x][y - 1];
            if (!l_cell.visited && l_cell.mark == '0'){
                list.add(l_cell);
            }
        }

        //right
        //determine whether cell of rightward direction is legal
        if (y != column){
            Cell r_cell = cells[x][y + 1];
            if (!r_cell.visited && r_cell.mark == '0'){
                list.add(r_cell);
            }
        }

        return list;
    }

    /**
     * determine whether the coordinate is legal
     *
     * @param x x coordinate
     * @param y y coordinate
     * @return
     */
    public boolean isPositionLegal(int x, int y){
        return x >= 0 && x <= row && y >= 0 && y <= column;
    }

    /**
     * print the final result if  cracked successful, otherwise it prompts that the cracking failed
     *
     * @param cells cells
     */
    public void printResult(Cell[][] cells) {
        if (cracked && cells.length != 0){
            for (Cell[] rows : cells) {
                for (Cell cell : rows) {
                    char mark = cell.mark;
                    if (mark == '1'){
                        mark = 'X';
                    }
                    if (mark == '0'){
                        mark = 'O';
                    }
                    System.out.print(mark + "  ");
                }
                System.out.println();
            }
        }else{
            System.out.println("破解失败");
        }
    }

}

三、控制台反馈

欢迎使用迷宫破解系统
请输入迷宫的行数
10
请输入迷宫的列数
10
迷宫的行数为:10迷宫的列数为:10
接下来请输入迷宫中的每一个单元格
'0'代表可通行,‘1'代表是墙体,不可通行
请输入第1行,共10个元素,中间不要有空格
1111111111
请输入第2行,共10个元素,中间不要有空格
1001110011
请输入第3行,共10个元素,中间不要有空格
1001100101
请输入第4行,共10个元素,中间不要有空格
1000000101
请输入第5行,共10个元素,中间不要有空格
1000011001
请输入第6行,共10个元素,中间不要有空格
1001110001
请输入第7行,共10个元素,中间不要有空格
1000010101
请输入第8行,共10个元素,中间不要有空格
1011000101
请输入第9行,共10个元素,中间不要有空格
1100001001
请输入第10行,共10个元素,中间不要有空格
1111111111
输入完毕,打印迷宫中
1  1  1  1  1  1  1  1  1  1  
1  0  0  1  1  1  0  0  1  1  
1  0  0  1  1  0  0  1  0  1  
1  0  0  0  0  0  0  1  0  1  
1  0  0  0  0  1  1  0  0  1  
1  0  0  1  1  1  0  0  0  1  
1  0  0  0  0  1  0  1  0  1  
1  0  1  1  0  0  0  1  0  1  
1  1  0  0  0  0  1  0  0  1  
1  1  1  1  1  1  1  1  1  1  
打印迷宫完毕
请再次输入迷宫的行数
10
请再次输入迷宫的列数
10
请输入起点坐标,前一个是x轴坐标,后一个是y轴坐标,按回车确认
8
8
请输入终点坐标,前一个是x轴坐标,后一个是y轴坐标,按回车确认
1
7
生成迷宫中
破解迷宫中
第1次尝试,坐标为:[7, 8]
第2次尝试,坐标为:[8, 8]
第3次尝试,坐标为:[8, 7]
第4次尝试,坐标为:[6, 8]
第5次尝试,坐标为:[5, 8]
第6次尝试,坐标为:[4, 8]
第7次尝试,坐标为:[3, 8]
第8次尝试,坐标为:[2, 8]
第9次尝试,坐标为:[4, 7]
第10次尝试,坐标为:[5, 7]
第11次尝试,坐标为:[5, 6]
第12次尝试,坐标为:[6, 6]
第13次尝试,坐标为:[7, 6]
第14次尝试,坐标为:[7, 5]
第15次尝试,坐标为:[8, 5]
第16次尝试,坐标为:[8, 4]
第17次尝试,坐标为:[7, 4]
第18次尝试,坐标为:[6, 4]
第19次尝试,坐标为:[6, 3]
第20次尝试,坐标为:[6, 2]
第21次尝试,坐标为:[5, 2]
第22次尝试,坐标为:[4, 2]
第23次尝试,坐标为:[3, 2]
第24次尝试,坐标为:[2, 2]
第25次尝试,坐标为:[1, 2]
第26次尝试,坐标为:[1, 1]
第27次尝试,坐标为:[2, 1]
第28次尝试,坐标为:[3, 1]
第29次尝试,坐标为:[4, 1]
第30次尝试,坐标为:[5, 1]
第31次尝试,坐标为:[6, 1]
第32次尝试,坐标为:[7, 1]
第33次尝试,坐标为:[3, 3]
第34次尝试,坐标为:[4, 3]
第35次尝试,坐标为:[4, 4]
第36次尝试,坐标为:[3, 4]
第37次尝试,坐标为:[3, 5]
第38次尝试,坐标为:[2, 5]
第39次尝试,坐标为:[2, 6]
第40次尝试,坐标为:[3, 6]
第41次尝试,坐标为:[1, 6]
第42次尝试,坐标为:[1, 7]
破解迷宫成功
X  X  X  X  X  X  X  X  X  X  
X  *  *  X  X  X  *  *  X  X  
X  *  *  X  X  *  *  X  O  X  
X  *  *  *  *  *  O  X  *  X  
X  *  *  *  *  X  X  *  *  X  
X  *  *  X  X  X  *  *  *  X  
X  *  *  *  *  X  *  X  *  X  
X  O  X  X  *  *  *  X  *  X  
X  X  O  O  *  *  X  O  *  X  
X  X  X  X  X  X  X  X  X  X  
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热爱旅行的小李同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值