数据结构_栈的应用_迷宫求解问题java实现

这篇文章讲述的是数据结构部分的迷宫求解问题的java实现,如有错误或者不当之处,还望各位大神批评指正。

问题描述

假设有一个迷宫使用二维数组,墙使用1表示,路径使用0表示,可达路径使用*表示,试写一算法计算出从起点到终点的一条可行路径。

算法分析

  1. 使用二维数组存放初始化的迷宫
  2. 借助栈来遍历迷宫的路径,栈中保存的是正确的路径
  3. 业务逻辑如下
while(栈不为空){
         if(找到终点){
             元素全部出栈,且将其标志位设为*
         }else{
             若没不是终点,则判断上下左右位置若合法则压进栈,若无路可走则出栈
         }                      
    }

代码实现

  • 迷宫的元素类型
/*迷宫单元的数据结构*/
    private class Cellimpl implements Cell{
        private int x ;                         //单元所在行
        private int y ;                         //单元所在列
        private boolean visited = false ;       //单元是否已被访问
        private char mark ;                     //单元格的类型,1表示墙,0表示路,*表示可行路径

/*省略get和set方法*/
  • 迷宫的数据结构类及相关方法
/*迷宫的数据结构*/
class Maze {
    /*迷宫大小*/
    final int LENGTH = 10 ;

    /*迷宫数据域*/
    private Cell CELLS [][] ;


    /*迷宫单元的数据结构*/
    private class Cellimpl implements Cell{
        private int x ;                         //单元所在行
        private int y ;                         //单元所在列
        private boolean visited = false ;       //单元是否已被访问
        private char mark ;                     //单元格的类型,1表示墙,0表示路,*表示可行路径

        public int getX() {
            return x;
        }
        public void setX(int x) {
            this.x = x;
        }
        public int getY() {
            return y;
        }
        public void setY(int y) {
            this.y = y;
        }
        public boolean isVisited() {
            return visited;
        }
        public void setVisited(boolean visited) {
            this.visited = visited;
        }
        public char getMark() {
            return mark;
        }
        public void setMark(char mark) {
            this.mark = mark;
        }
    }
    /**
     * @explain createMaze方法: 创建迷宫
     * @param maze 传入表示迷宫的二维数组
     * @throws 
     * @author 叶清逸
     * @date 2018年7月31日 下午8:49:23
     */
    public void create(char maze[][]){
        /*为迷宫的数据域分配空间*/
        CELLS = new Cell[LENGTH][LENGTH] ;

        /*将二维数组类型转换为迷宫*/
        for(int i=0 ; ifor(int j=0 ; j/*获取数组元素*/
                Cell cell = new Cellimpl() ;
                /*初始化该迷宫元素*/
                char c = maze[i][j] ;
                /*将二维数组元素转换为迷宫元素*/
                if(c == '1'){
                    cell.setMark('1') ;                         //设置标识位1
                }else if(c == '0'){
                    cell.setMark('0') ;                         //设置标识为0
                }
                cell.setX(i) ;                                  //设置横坐标
                cell.setY(j) ;                                  //设置纵坐标
                cell.setVisited(false);                         //设置访问标识

                /*将该元素放入迷宫对应位置*/
                CELLS[i][j] = cell ;
            }
        }
    }
    /**
     * @explain print方法: 打印迷宫
     * @throws 
     * @author 叶清逸
     * @date 2018年7月31日 下午9:20:35
     */
    public void print(){
        /*遍历整个迷宫的数据域*/
        for(int i=0 ; ifor(int j=0 ; j/*打印该迷宫单元的标识*/
                System.out.print(CELLS[i][j].getMark()+" ");
            }
            System.out.println();
        }
    }
    public Cell[][] getCELLS() {
        return CELLS;
    }
}
  • 寻路算法(核心代码)
/**
     * @explain findPath方法: 计算迷宫起点到终点的路径
     * @param maze 要寻路迷宫
     * @param sx 起点的横坐标
     * @param sy 起点的纵坐标
     * @param ex 终点的横坐标
     * @param ey 终点的纵坐标
     * @return boolean 起点到终点间是否存在路径,若存在返回true,不存在返回false
     * @throws 
     * @author 叶清逸
     * @date 2018年7月31日 下午9:29:07
     */
    private static boolean findPath(Maze maze , int sx , int sy , int ex , int ey){
        boolean flag = false ;
        /*获取迷宫的数据域*/
        Cell[][]cells = maze.getCELLS() ;
        /*初始化辅助栈*/
        Stack stack = new LinkStack() ;
        stack.init();
        /*获取起点与终点*/
        Cell startCell = cells[sx][sy] ;
        Cell endCell = cells[ex][ey] ;
        /*将起点加入栈中*/
        stack.push(startCell);
        startCell.setVisited(true);                     //设置起点已被访问
        /*穷举出所有情况*/
        while(!stack.isEmpty()){
            /*取出栈顶元素,若其是终点,则顺序将所访问的节点标识制为* */
            Cell curCell = (Cell)stack.getTop() ;
            if(curCell == endCell){
                while(!stack.isEmpty()){
                    Cell cell = (Cell)stack.pop() ;
                    cell.setMark('*') ;
                }
                flag = true ;
            }else{
                /*获取该点的横坐标和纵坐标*/
                int x = curCell.getX() ;
                int y = curCell.getY() ;
                /*若栈顶元素不为终点,判断上下左右位置是否合法,若合法压入栈*/
                if(cells[x+1][y].getMark() == '0' && cells[x+1][y].isVisited() == false){           //右边
                    stack.push(cells[x+1][y]);
                    cells[x+1][y].setVisited(true);
                }else if(cells[x][y+1].getMark() == '0' && cells[x][y+1].isVisited() == false){     //下边
                    stack.push(cells[x][y+1]);
                    cells[x][y+1].setVisited(true);
                }else if(cells[x-1][y].getMark() == '0' && cells[x-1][y].isVisited() == false){     //左边
                    stack.push(cells[x-1][y]);
                    cells[x-1][y].setVisited(true);
                }else if(cells[x][y-1].getMark() == '0' && cells[x][y-1].isVisited() == false){
                    stack.push(cells[x][y-1]);                                                      //上边
                    cells[x][y-1].setVisited(true);
                }else{
                    stack.pop() ;                                                                   //若为死路则退栈
                }
            }
        }
        return flag ;
    }

程序完整代码

package stack_question;

import stack.LinkStack;
import stack.Stack;

/**
 * @author 叶清逸
 * @date 2018年7月31日下午8:00:38
 * @version 1.0
 * @project stack_question
 */
public class Q2_MazePath {
    /**
     * 问题描述:假设有一个迷宫使用二维数组,墙使用1表示,路径使用0表示,可达路径使用*表示,试写一算法计算
     *          出从起点到终点的一条可行路径。
     * 
     * 算法分析:1. 使用二维数组存放初始化的迷宫
     *          2. 借助栈来遍历迷宫的路径,栈中保存的是正确的路径
     *          3. 业务逻辑如下
     *                      while(栈不为空){
     *                          if(找到终点){
     *                                 元素全部出栈,且将其标志位设为*
     *                          }else{
     *                              若没不是终点,则判断上下左右位置若合法则压进栈,若无路可走则出栈
     *                          }
     *                      }
     */
    public static void main(String[] args) {
        /*初始化迷宫的二维数组*/
        char [][] mazearr = {{'1','1','1','1','1','1','1','1','1','1'} ,
                             {'1','0','0','1','0','0','0','1','0','1'} ,
                             {'1','0','0','1','0','0','0','1','0','1'} ,
                             {'1','0','0','0','0','1','1','0','0','1'} ,
                             {'1','0','1','1','1','0','0','0','0','1'} ,
                             {'1','0','0','0','1','0','0','0','0','1'} ,
                             {'1','0','1','0','0','0','1','0','0','1'} ,
                             {'1','0','1','1','1','0','1','1','0','1'} ,
                             {'1','1','0','0','0','0','0','0','0','1'} ,
                             {'1','1','1','1','1','1','1','1','1','1'} } ; 

        /*初始化迷宫*/
        Maze maze = new Maze() ;
        maze.create(mazearr) ;
        System.out.println("迷宫初始状态:");
        maze.print();

        /*计算从起点到终点的路径*/
        boolean flag = findPath(maze, 1, 1, 8, 8) ;
        if(flag){
            System.out.println("路径已找到,如下:");
            maze.print();
        }else{
            System.out.println("起点与终点之间没有可达路径");
        }

    }

    /**
     * @explain findPath方法: 计算迷宫起点到终点的路径
     * @param maze 要寻路迷宫
     * @param sx 起点的横坐标
     * @param sy 起点的纵坐标
     * @param ex 终点的横坐标
     * @param ey 终点的纵坐标
     * @return boolean 起点到终点间是否存在路径,若存在返回true,不存在返回false
     * @throws 
     * @author 叶清逸
     * @date 2018年7月31日 下午9:29:07
     */
    private static boolean findPath(Maze maze , int sx , int sy , int ex , int ey){
        boolean flag = false ;
        /*获取迷宫的数据域*/
        Cell[][]cells = maze.getCELLS() ;
        /*初始化辅助栈*/
        Stack stack = new LinkStack() ;
        stack.init();
        /*获取起点与终点*/
        Cell startCell = cells[sx][sy] ;
        Cell endCell = cells[ex][ey] ;
        /*将起点加入栈中*/
        stack.push(startCell);
        startCell.setVisited(true);                     //设置起点已被访问
        /*穷举出所有情况*/
        while(!stack.isEmpty()){
            /*取出栈顶元素,若其是终点,则顺序将所访问的节点标识制为* */
            Cell curCell = (Cell)stack.getTop() ;
            if(curCell == endCell){
                while(!stack.isEmpty()){
                    Cell cell = (Cell)stack.pop() ;
                    cell.setMark('*') ;
                }
                flag = true ;
            }else{
                /*获取该点的横坐标和纵坐标*/
                int x = curCell.getX() ;
                int y = curCell.getY() ;
                /*若栈顶元素不为终点,判断上下左右位置是否合法,若合法压入栈*/
                if(cells[x+1][y].getMark() == '0' && cells[x+1][y].isVisited() == false){           //右边
                    stack.push(cells[x+1][y]);
                    cells[x+1][y].setVisited(true);
                }else if(cells[x][y+1].getMark() == '0' && cells[x][y+1].isVisited() == false){     //下边
                    stack.push(cells[x][y+1]);
                    cells[x][y+1].setVisited(true);
                }else if(cells[x-1][y].getMark() == '0' && cells[x-1][y].isVisited() == false){     //左边
                    stack.push(cells[x-1][y]);
                    cells[x-1][y].setVisited(true);
                }else if(cells[x][y-1].getMark() == '0' && cells[x][y-1].isVisited() == false){
                    stack.push(cells[x][y-1]);                                                      //上边
                    cells[x][y-1].setVisited(true);
                }else{
                    stack.pop() ;                                                                   //若为死路则退栈
                }
            }
        }
        return flag ;
    }
}

/*迷宫元素的接口*/
interface Cell{
    //获取元素的横坐标
    public int getX() ;
    //设置元素的横坐标
    public void setX(int x) ;
    //获取元素的纵坐标
    public int getY() ;
    //设置元素的纵坐标
    public void setY(int y) ;
    //是否被访问过
    public boolean isVisited() ;
    //访问元素节点
    public void setVisited(boolean visited) ;
    //获取元素标识
    public char getMark() ;
    //设置元素标识
    public void setMark(char mark) ;
}

/*迷宫的数据结构*/
class Maze {
    /*迷宫大小*/
    final int LENGTH = 10 ;

    /*迷宫数据域*/
    private Cell CELLS [][] ;


    /*迷宫单元的数据结构*/
    private class Cellimpl implements Cell{
        private int x ;                         //单元所在行
        private int y ;                         //单元所在列
        private boolean visited = false ;       //单元是否已被访问
        private char mark ;                     //单元格的类型,1表示墙,0表示路,*表示可行路径

        public int getX() {
            return x;
        }
        public void setX(int x) {
            this.x = x;
        }
        public int getY() {
            return y;
        }
        public void setY(int y) {
            this.y = y;
        }
        public boolean isVisited() {
            return visited;
        }
        public void setVisited(boolean visited) {
            this.visited = visited;
        }
        public char getMark() {
            return mark;
        }
        public void setMark(char mark) {
            this.mark = mark;
        }
    }
    /**
     * @explain createMaze方法: 创建迷宫
     * @param maze 传入表示迷宫的二维数组
     * @throws 
     * @author 叶清逸
     * @date 2018年7月31日 下午8:49:23
     */
    public void create(char maze[][]){
        /*为迷宫的数据域分配空间*/
        CELLS = new Cell[LENGTH][LENGTH] ;

        /*将二维数组类型转换为迷宫*/
        for(int i=0 ; ifor(int j=0 ; j/*获取数组元素*/
                Cell cell = new Cellimpl() ;
                /*初始化该迷宫元素*/
                char c = maze[i][j] ;
                /*将二维数组元素转换为迷宫元素*/
                if(c == '1'){
                    cell.setMark('1') ;                         //设置标识位1
                }else if(c == '0'){
                    cell.setMark('0') ;                         //设置标识为0
                }
                cell.setX(i) ;                                  //设置横坐标
                cell.setY(j) ;                                  //设置纵坐标
                cell.setVisited(false);                         //设置访问标识

                /*将该元素放入迷宫对应位置*/
                CELLS[i][j] = cell ;
            }
        }
    }
    /**
     * @explain print方法: 打印迷宫
     * @throws 
     * @author 叶清逸
     * @date 2018年7月31日 下午9:20:35
     */
    public void print(){
        /*遍历整个迷宫的数据域*/
        for(int i=0 ; ifor(int j=0 ; j/*打印该迷宫单元的标识*/
                System.out.print(CELLS[i][j].getMark()+" ");
            }
            System.out.println();
        }
    }
    public Cell[][] getCELLS() {
        return CELLS;
    }
}

样例输出

迷宫初始状态:
1 1 1 1 1 1 1 1 1 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 1 0 0 0 1 0 1 
1 0 0 0 0 1 1 0 0 1 
1 0 1 1 1 0 0 0 0 1 
1 0 0 0 1 0 0 0 0 1 
1 0 1 0 0 0 1 0 0 1 
1 0 1 1 1 0 1 1 0 1 
1 1 0 0 0 0 0 0 0 1 
1 1 1 1 1 1 1 1 1 1 
路径已找到,如下:
1 1 1 1 1 1 1 1 1 1 
1 * 0 1 0 0 0 1 0 1 
1 * 0 1 0 0 0 1 0 1 
1 * 0 0 0 1 1 0 0 1 
1 * 1 1 1 0 0 0 0 1 
1 * * * 1 0 0 0 0 1 
1 0 1 * * * 1 0 0 1 
1 0 1 1 1 * 1 1 0 1 
1 1 0 0 0 * * * * 1 
1 1 1 1 1 1 1 1 1 1 

程序的问题

由于探索路径的方法不同造成的结果不同,如程序中遍历当前节点的上下左右节点时采取的是右下左上的方案,该方案适用于起点在左上,终点在右下的路径探寻,若探寻起点在右下终点在左上的路径时则会造成走过多的路,如若将上例的终点改为(5,5)则会造成如下情况:

路径已找到,如下:
1 1 1 1 1 1 1 1 1 1 
1 * 0 1 0 0 0 1 0 1 
1 * 0 1 0 0 0 1 0 1 
1 * 0 0 0 1 1 * * 1 
1 * 1 1 1 * * * * 1 
1 * * * 1 * * * * 1 
1 0 1 * * * 1 0 * 1 
1 0 1 1 1 * 1 1 * 1 
1 1 0 0 0 * * * * 1 
1 1 1 1 1 1 1 1 1 1 

这样虽然可以正确找到路径,但也会多走很多路

你可能感兴趣的:(数据结构)