51. N 皇后 | 回溯算法 | 暴力求解 | 组合 | 选择

力扣打卡:51. N 皇后

解题思路

用数组来代替字符串数组

回溯算法的框架


List<T> track = new LinkedList<>();
List<List<T>> res = new ArrayList<>();

public void backtrack(){
    if(终止条件) {
        记录结果
        return;
    }
    for(所有的可能){
        1. 做结果
        2. backtrack();
        3. 撤销选择
    }
}

N皇后是指不能在同行同列同斜杠

遇到了遇到求出所有的可能, 但是无子问题的重叠, 那么需要用的就是暴力求解, 也就是使用回溯

按照框架去凑,大的方向就不会错

回溯 就是 N 叉树的遍历, 做出选择,然后进行下一个递归,再进行撤销选择

终止条件是给定的边界,如 数组索引不越界

代码

class Solution {

    List<String> track = new LinkedList<>();
    List<List<String>> res = new LinkedList<>();

    public List<List<String>> solveNQueens(int n) {
        // 列出所有的可能 / 得到所有的结果集  没有子问题的分解  -- > 暴力求解 -- > 回溯算法
        // Java中的字符操作实在是有点麻烦,用整数数组来代替
        int[][] board = new int[n][n];
        backtrack(board, 0);  // 从 第 0 行开始
        return res;
    }
    // 首先写出终止条件
    // 判断终止条件的流程, 回溯就是递归, 多出了做选择的流程
    // 当遍历到叶子节点的时候就加入结果集,也就是终止条件
    // 也就是从根结点出发(第一行)到叶子节点(最后一行)的,到最后一行那么就是叶子节点了,一行一行的判断穷举
    // 如果超出了最后一行,那么说明这肯定是一个可行解,因为最后一行都做出选择了(如果不满足,会直接跳过,而不是做选择)
    public void backtrack(int[][] board, int i){
        if(i==board.length) { // 如果已经超出了索引,那么说明此时的结果是一个可行解
            res.add(new LinkedList<>(track));
            return ;
        }
        StringBuilder line = new StringBuilder();  // 记录每一行的数据
        for(int j=0; j<board[i].length; j++){
            if(!isValid(board, i, j)) { // 检查同行同列同斜杠是否有皇后,如果无效,那么跳过
                line.append("."); // 跳过当前的棋格,也就是 "."
                continue;
            }
            line.append("Q");// 做出选择,也即是此时的位置加入皇后
            board[i][j] = 1;
            while(line.length() < board[i].length) line.append("."); // 此时的位置不一定是最后一列,那么需要加入空棋格
            track.add(line.toString()); // 加入

            backtrack(board, i+1); // 传递下一行,因为不能同行同列同斜杠
            
            track.remove(track.size()-1); // 撤销最后一个
            board[i][j] = 0;
            line.setLength(line.indexOf("Q"));// 找到Q的位置,撤销选择
            line.append("."); // 将此时的位置重新加入空棋格
        }
    }
    public boolean isValid(int[][] board, int i, int j){
        
        // 检查同行
        for(int y=0; y<board[i].length; y++) if(board[i][y] == 1) return false;

        // 检查同列
        for(int x=0; x<board.length; x++) if (board[x][j] == 1) return false;

        // 检查左上方
        for(int x=i-1,y=j-1; x>=0 && y>=0; x--, y--){
            if(board[x][y]==1) return false;
        }
        
        // 检查右上方
        for(int x=i-1,y=j+1; x>=0 && y<board[x].length; x--, y++){
            if(board[x][y] == 1) return false;
        }

        // 不用检查左下方和右下方,因为是按照一行一行的进行穷举,下方还是空棋格,所以不需要检查
        // 当然检查了也没事,但是会增加时间复杂度
        return true; 
    }
    
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值