递归篇+附加几个题目

目录

1.八皇后问题

2.N皇后之字符串

3.全排列


这几个题的解法思路一样,可以总结出几个点。

共同修改共享数据,负责递归的函数通常有几个相似的步骤

1.出递归的条件

2.判断是否能进入递归

3.做选择,打比方,八皇后中当前层皇后放在哪个位置。

4.进入下一层。

5.回撤,即发现当前行不通时,将该层的修改改回原来的状态。

完全暴力算法八皇后应该要执行8的8次方,在递归时做合法性判断能缩小不必要的执行次数

回溯发生在「递归调用返回后」,且仅当当前选择的路径走不通(无合法后续)或已经探索完该路径的所有可能性时。用来判断其他的情况。

一.八皇后问题

public class Queen8 {
    //定义一个max表示有多少个皇后
    int max = 4;
    int[] array = new int[max];
    static int count = 0;
    public static void main(String[] args) {
        //测试8皇后是否正确
        Queen8 queen8 = new Queen8();
        long startTime = System.currentTimeMillis();
        queen8.check(0);
        long endTime = System.currentTimeMillis();
        long costTime = endTime - startTime;
        System.out.printf("一共有%d解法",count);
        System.out.println();
        System.out.println(costTime);
    }
    //编写方法,放置第n个皇后
    private void check(int n){
        if (n==max){
            print();
            return;
        }
        //依次放入皇后并且判断是否冲突
        for (int i = 0; i < max; i++) {
            //先把当前这个皇后n,放在该行的第一列
            array[n] = i;
            //判断当放置到第n个皇后是否冲突
            if (judge(n)){
                check(n+1);
            }
            //如果冲突,并且i++,放置到本行的后一个位置
        }
    }


    //查看当我们放置第n个皇后,就去检测该皇后是否和前面拜访的皇后冲突
    /**
     * @param n 表示第n个皇后
     */
    private boolean judge(int n){
        for (int i = 0; i < n; i++) {
            //Math.abs(n-i) == Math.abs(array[n]-array[i])
            //如果在斜线位置,皇后之间列之差就等于行之差
            if (array[i] == array[n] ||Math.abs(n-i) == Math.abs(array[n]-array[i])){
                return false;
            }
        }
        return true;
    }
    //方法,将皇后摆放的位置输出
    private void print(){
        count++;
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");
        }
        System.out.println();
    }
}

二.N皇后之字符串

        这个跟上一个八皇后不同的是不再有下标能代表行,值能代表列的数组了,需要自己设置行列,并且考虑怎么才能很好的做合法性判断,还需要正确的添加字符集合。

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        Solution solution = new Solution();
        List<List<String>> solveNQueens = solution.solveNQueens(4);
        System.out.println(solveNQueens);
    }
}

class Solution {
    private List<List<String>> result = new ArrayList<>();// 存储所有合法的棋盘结果

    public List<List<String>> solveNQueens(int n) {
        List<StringBuilder> board = new ArrayList<>(); // 存储当前棋盘状态(行优先)
        for (int i = 0; i < n; i++) {
            StringBuilder row = new StringBuilder();
            row.append(".".repeat(n)); // 初始化每行都是n个点
            board.add(row);
        }
        backtrack(board, 0); // 从第0行开始回溯
        return result;
    }

    /**
     * 回溯函数:尝试在第row行放置皇后
     * @param board 当前棋盘状态
     * @param row   当前要放置皇后的行号
     */
    public void backtrack(List<StringBuilder> board, int row) {
        int n = board.size();
        // 递归终止:所有行都放置了皇后,保存当前棋盘
        if (row == n) {
            List<String> validBoard = new ArrayList<>();
            for (StringBuilder sb : board) {
                validBoard.add(sb.toString());
            }
            result.add(validBoard);
            return;
        }

        // 遍历当前行的每一列,尝试放置皇后
        for (int col = 0; col < n; col++) {
            // 检查当前位置(row, col)是否合法
            if (!isValid(col, row, board)) {
                continue;
            }
            // 1. 做选择:在(row, col)放皇后
            board.get(row).setCharAt(col, 'Q');
            // 2. 递归:处理下一行,探索这个选择的所有后续可能性
            backtrack(board, row + 1);
            // 3. 撤销选择:把皇后拿掉,恢复为点,尝试当前行的下一列
            board.get(row).setCharAt(col, '.');
        }
    }

    /**
     * 检查(row, col)位置放置皇后是否合法
     * @param col   列号
     * @param row   行号
     * @param board 棋盘状态
     * @return 合法返回true,否则false
     */
    public boolean isValid(int col, int row, List<StringBuilder> board) {
        int n = board.size();

        // 1. 检查同一列是否有皇后(只检查当前行上方的行)
        for (int i = 0; i < row; i++) {
            if (board.get(i).charAt(col) == 'Q') {
                return false;
            }
        }

        // 2. 检查左上对角线(行-1,列-1)
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if (board.get(i).charAt(j) == 'Q') {
                return false;
            }
        }

        // 3. 检查右上对角线(行-1,列+1)
        for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
            if (board.get(i).charAt(j) == 'Q') {
                return false;
            }
        }

        return true;
    }
}

三.全排列

        这个的思路和八皇后类似,但要思考什么作为合法性判断来缩小范围,什么作为递归出来的条件,我的选择,递归,回撤该怎么做。

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    boolean[] user; 
    public List<List<Integer>> permute(int[] nums) {
        user = new boolean[nums.length];
        backTrack(nums);
        return result;
    }

    public void backTrack(int[] nums){
        if(path.size()==nums.length){
            result.add(new ArrayList<>(path));
            return;
        }
        
        for(int i = 0;i<nums.length;i++){
            if(!user[i]){
                path.add(nums[i]);
                //选择
                user[i]=true;
                //递归
                backTrack(nums);
                //回撤
                path.remove(path.size()-1);
                user[i]=false;
            }
        }
    }
}

四.总结

        第一个题是看韩顺平老师的数据结构课程跟着敲出来的,第二三题,我是根据ai给出思路和代码,慢慢理解敲出来的,虽然惭愧没完整自己想出一题,但自己写了三题目有一点心得写在这里。

以后会越来越好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值