回溯专题——leetcode51. n皇后问题 Hard

1.题目描述

The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space, respectively.

Example 1:

Input: n = 4
Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above

Example 2:

Input: n = 1
Output: [["Q"]]

Constraints:

  • 1 <= n <= 9

2.题解

这里参考了中文leetcode平台的官方解答,

思路:2个皇后不能在同行、同列、两个方向的斜线上,n*n的棋盘必须放n个皇后,所以每行有1个皇后,第1行的皇后有n个位置可选,第2行有最多n-1个位置可选,依次类推,时间复杂度为O(N!);

判断2个皇后是否在同一条斜线上的方法:对于“左上-右下”方向,每一条斜线上的点的行列坐标之差相同,因此 “行-列” 可以代表这个方向的所有斜线;同理,对于“右上-左下”方向,“行+列” 可以代表这个方向的所有斜线。

时间复杂度:O(N!)  (因为每行必有一个皇后,所以问题简化为列下标 0-n-1 的全排列问题。)

空间复杂度:  O(N)  (开辟一个大小为n的数组存储每行皇后的列下标)

class Solution {
    public List<List<String>> solveNQueens(int n) {
        List<List<String>> solutions = new ArrayList<>();
        //存储每行皇后所在的列下标
        int[] queens = new int[n];
        Arrays.fill(queens, -1);
        //记录 左上-右下方向是否有皇后
        Set<Integer> diagonals1 = new HashSet<Integer>();
        //记录 右上-左下方向是否有皇后
        Set<Integer> diagonals2 = new HashSet<Integer>();
        //记录 该列是否有皇后
        Set<Integer> columns = new HashSet<Integer>();
        backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
        return solutions;
    }
    
    public void backtrack(List<List<String>> solutions, int[] queens, int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2){
        if(row == n){
            List<String> board = generateBoard(queens, n);
            solutions.add(board);
            return;
        }
        
        for(int i = 0; i < n; i++){
            //除去非法选择
            if(columns.contains(i)){
                continue;
            }
            int diagonal1 = row - i;
            if(diagonals1.contains(diagonal1)){
                continue;
            }
            int diagonal2 = row + i;
            if(diagonals2.contains(diagonal2)){
                continue;
            }
            //做选择
            queens[row] = i;
            columns.add(i);
            diagonals1.add(diagonal1);
            diagonals2.add(diagonal2);
            //进入决策树的下一层
            backtrack(solutions, queens, n, row+1, columns, diagonals1, diagonals2);
            //撤销选择
            queens[row] = -1;
            columns.remove(i);
            diagonals1.remove(diagonal1);
            diagonals2.remove(diagonal2);
        }
        
    }
    
    //生成要求的结果格式
    public List<String> generateBoard(int[] queens, int n){
        List<String> board = new ArrayList<String>();
        for(int i = 0; i < n; i++){
            char[] row = new char[n];
            Arrays.fill(row, '.');
            row[queens[i]] = 'Q';
            board.add(new String(row));
        }
        return board;
    }
    
}

3.解题框架和思路

(1)回溯问题的通用框架

对于n皇后问题,路径就是queens里存储的每行皇后已经放好的列下标,选择列表则是该行符合要求的剩余的列下标,n皇后的总体思路是从第1行到第n行确定每个皇后的列下标。

回溯问题的通用框架:

result = []
def backtrack ( 路径 , 选择列表 ) :
     if 满⾜结束条件 :
             result.add( 路径 )
             return
    for 选择 in 选择列表 :
            做选择
            backtrack( 路径 , 选择列表 )
            撤销选择

 

(2)n皇后问题的解

n的值解的个数
11
20
30
42
510
64
740
892
9352

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值