[Algorithm]Java 版递归算法解迷宫问题哈诺塔问题八皇后问题

Posted Spring-_-Bear

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Algorithm]Java 版递归算法解迷宫问题哈诺塔问题八皇后问题相关的知识,希望对你有一定的参考价值。

递归算法

  1. 何为递归:递归就是在过程或函数里自己调用自己
  2. 递归解决的问题:数据的定义是按递归定义的,如Fibonacci函数;问题解法按递归算法实现,如Hanoi问题;数据的结构形式是按递归定义的,如二叉树、广义表等
  3. 使用递归需要注意的问题:递归调用一次方法时就会开辟一个独立的栈空间,各个栈空间中局部变量相互独立互不影响;如果递归方法中的参数是引用类型则递归调用的所有方法共享该变量;递归过程必须趋向退出递归的条件逼近;当一个方法执行完毕或遇到 return 就会返回到方法调用处,谁调用就将返回值返回给谁

一、迷宫问题

1. 迷宫问题描述

迷宫问题描述:有一个迷宫地图,有一些可达的位置,也有一些不可达的位置(障碍、墙壁、边界)。从一个位置到下一个位置只能通过向上(或者向右、或者向下、或者向左)走一步来实现,从起点出发,如何找到一条到达终点的通路

2. 迷宫问题代码实现

/**
 * @author Spring-_-Bear
 * @datetime 2022/3/11 20:10
 */
public class MazeGame 
    /**
     * The height of the game map.
     */
    private static final int ROWS = 25;
    /**
     * The width of the game map.
     */
    private static final int COLS = 25;
    /**
     * The two-dimensional array stands for the game map.
     */
    private static final int[][] MAP = new int[ROWS][COLS];

    public static int[][] getMap() 
        return MAP;
    

    /**
     * The state of current location.
     */
    enum State 
        /**
         * COULD_GO - 0: The current location was never passed by, could go with judge success.
         * OBSTACLE - 1: There is an obstacle in the current location, don't go.
         * ACCESSIBLE - 2: The current location is safe, could go.
         * DON_NOT_GO_AGAIN: You have judged the current location, don't go again.
         */
        COULD_GO, OBSTACLE, ACCESSIBLE, DON_NOT_GO_AGAIN
    

    /**
     * Initialize the boundary of game map, value is 1.
     * Randomly insert obstacles,value is 1.
     *
     * @param map Two-dimensional array.
     */
    public void initMap(int[][] map) 

        // Initialize the boundary of game map, values is 1.
        // Initialize the value of the first row of the two-dimensional game map to 1
        for (int i = 0; i < MazeGame.COLS; i++) 
            map[0][i] = 1;
        
        // Initialize the element value of the last row of the two-dimensional game map to 1
        for (int i = 0; i < MazeGame.COLS; i++) 
            map[MazeGame.ROWS - 1][i] = 1;
        
        // Initialize the value of the first column of the two-dimensional game map to 1
        for (int i = 0; i < MazeGame.ROWS; i++) 
            map[i][0] = 1;
        
        // Initialize the value of the last column of the two-dimensional game map to 1
        for (int i = 0; i < MazeGame.ROWS; i++) 
            map[i][MazeGame.COLS - 1] = 1;
        

        // Randomly insert obstacles,values is 1.
        for (int i = 0; i < MazeGame.ROWS; i++) 
            // num: Initialized number of obstacles in each row.
            int num = ((int) (Math.random() * MazeGame.COLS) + 1) / 2;
            for (int j = 0; j < num; j++) 
                // Initial position
                int location = (int) (Math.random() * MazeGame.COLS);
                map[i][location] = State.OBSTACLE.ordinal();
            
        
    


    /**
     * Print the two-dimensional game map.
     *
     * @param map Two-dimensional array.
     */
    public void printMap(int[][] map) 
        for (int i = 0; i < MazeGame.COLS; i++) 
            for (int j = 0; j < MazeGame.COLS; j++) 
                System.out.print(map[i][j] + " ");
            
            System.out.println();
        
        System.out.println();
    

    /**
     * Try to find the way to go through the maze.
     *
     * @param map The two-dimensional game map.
     * @param row The abscissa of the initial position.
     * @param col The ordinate of the initial position.
     * @return true or false
     */
    public boolean findWay(int[][] map, int row, int col) 
        // Recursive exit, reaching the end, game is over
        if (map[MazeGame.ROWS - 2][MazeGame.COLS - 2] == State.ACCESSIBLE.ordinal()) 
            return true;
         else 
            // Did not find the recursive exit, continue to recursively find the exit
            // Current location was never passed by, you should judge whether it could go.
            if (map[row][col] == State.COULD_GO.ordinal()) 
                // First, we assumed to be able to go through, make the value is 2
                map[row][col] = State.ACCESSIBLE.ordinal();

                // To the right of the current position is a path
                if (findWay(map, row, col + 1)) 
                    return true;
                 else if (findWay(map, row + 1, col)) 
                    // Below the current position is a pathway
                    return true;
                 else if (findWay(map, row, col - 1)) 
                    // To the left of the current position is a path
                    return true;
                 else if (findWay(map, row - 1, col)) 
                    // Above the current position is a pathway
                    return true;
                 else 
                    // There is no accessible way of the current location, change its value to 3.
                    map[row][col] = State.DON_NOT_GO_AGAIN.ordinal();
                    return false;
                
             else 
                // map[row][col] == 1 || 2 || 3
                return false;
            
        
    

    public static void main(String[] args) 
        MazeGame mazeGame = new MazeGame();
        mazeGame.initMap(MazeGame.getMap());
        System.out.println("The initial game map:");
        mazeGame.printMap(MazeGame.getMap());
        if (mazeGame.findWay(MazeGame.getMap(), 1, 1)) 
            System.out.println("You Win!!!");
         else 
            System.out.println("You Lose!!!");
        
        mazeGame.printMap(MazeGame.getMap());
    

3. 迷宫问题运行示例

二、汉诺塔问题

1. 汉诺塔问题描述

相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如图1)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上

2. 汉诺塔问题代码实现

/**
 * @author Spring-_-Bear
 * @datetime 2022/3/11 20:11
 */
public class HanoiTower 
    private int cnt = 0;
    /**
     * 汉诺塔盘子移动
     *
     * @param num    盘子数量
     * @param begin  初始状态所有盘子放在的柱子
     * @param middle 中间柱子
     * @param target 目标柱子:即把所有的盘子从 begin 移到 target 上
     * @return 盘子一共移动的次数
     */
    public int move(int num, char begin, char middle, char target) 
        cnt++;
        // 如果只有一个盘,直接从 begin 移动到 target
        if (num == 1) 
            System.out.println(begin + " -> " + target);
         else 
            // 先将剩下的 num - 1 个盘子从 begin 移动到 middle,中间过程借助 target 柱
            move(num - 1, begin, target, middle);
            // 将 begin 上的最后一个盘移动到 target
            System.out.println(begin + " -> " + target);
            // 再将 middle 柱上剩下的 num -1 个盘从 middle 移动到 target,中间过程借助 begin 柱
            move(num - 1, middle, begin, target);
        
        return cnt;
    

    public static void main(String[] args) 
        char a = 'a';
        char b = 'b';
        char c = 'c';
        Scanner scanner = new Scanner(System.in);
        System.out.print("Input the number of plates:");
        System.out.println("The total number of moves is " + new HanoiTower().move(scanner.nextInt(), a, b, c));
    

3. 汉诺塔问题运行示例

三、八皇后问题

1. 八皇后问题描述

在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果

2. 八皇后问题代码实现

/**
 * @author Spring-_-Bear
 * @datetime 2022/3/12 22:33
 */
public class EightQueens 
    /**
     * 皇后数量
     */
    private static final int QUEENS = 8;
    /**
     * 存放皇后摆放位置,array[i] 含义:在第 i 行,皇后摆放在第 array[i] 个位置
     */
    private final int[] array = new int[QUEENS];
    /**
     * 皇后摆法
     */
    private int solution;

    public int getSolution() 
        return solution;
    

    /**
     * 放置皇后
     *
     * @param queenNum 皇后编号
     */
    public void place(int queenNum) 
        if (queenNum == QUEENS) 
            print();
            ++solution;
            return;
        

        // 依次放置皇后,同时判断布局是否冲突
        for (int i = 0; i < QUEENS; i++) 
            // 先将当前皇后放到该行的第 1 列,并判断是否冲突
            array[queenNum] = i;
            if (isConflict(queenNum)) 
                // 不冲突接着放置皇后
                place(queenNum + 1);
            
            // 冲突则当前皇后后移一个位置
        
    

    /**
     * 当摆放编号为第 queenNum 的皇后时,判断是否与棋盘上的皇后布局冲突
     * 冲突条件:两两皇后之间不能位于同一行、同一列、同一斜线
     *
     * @param queenNum 皇后编号
     * @return true - 冲突
     */
    private boolean isConflict(int queenNum) 
        for (int i = 0; i < queenNum; i++) 
            // 判断两个皇后是否在同一列
            // 注:无需判断是否在同一行,因为 i 自增依次表示第 i + 1 行
            if (array[i] == array[queenNum]) 
                return false;
            
            // 判断是否在同一斜线上
            if (Math.abs(queenNum - i) == Math.abs(array[queenNum] - array[i])) 
                return false;
            
        
        return true;
    

    /**
     * 输出皇后布局
     */
    private void print() 
        for (int location : array) 
            System.out.print(location + " ");
        
        System.out.println();
    

    public static void main(String[] args) 
        EightQueens eightQueens = new EightQueens();
        eightQueens.place(0);
        System.out.println("皇后摆放方式共有 " + eightQueens.getSolution() + " 种.");
    

3. 八皇后问题运行示例

以上是关于[Algorithm]Java 版递归算法解迷宫问题哈诺塔问题八皇后问题的主要内容,如果未能解决你的问题,请参考以下文章

回溯算法走迷宫(Java版)

Java数据结构之回溯算法的递归应用迷宫的路径问题

深度优先搜索

超详解的迷宫问题(Java版)

Day560.递归 -数据结构和算法Java

C语言 版 数据结构