51.n-queens
The n-queens puzzle is the problem of placing n queens on an n×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:
Input: 4
Output: [
[".Q..", // Solution 1
"...Q",
"Q...",
"..Q."],
["..Q.", // Solution 2
"Q...",
"...Q",
".Q.."]
]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above.
代码
基于集合的回溯
package n_queens;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击
* 注意,1.每一行要都放一个皇后,棋盘是方形的
*
*/
public class Solution1 {
public List<List<String>> solveNQueens(int n) {
List<List<String>> solutions = new ArrayList<List<String>>();
int[] queens = new int[n];//index表示第几行,index对应的数字表示第几列
Arrays.fill(queens, -1);//填充为-1
Set<Integer> columns = new HashSet<Integer>();//列集合
Set<Integer> diagonals1 = new HashSet<Integer>();//斜线为从左上到右下方向
Set<Integer> diagonals2 = new HashSet<Integer>();//斜线为从右上到左下方向
backtrack(solutions, queens, n, 0, columns, diagonals1, diagonals2);
return solutions;
}
/**
* 基于集合的回溯,通过三个集合来判断是否包含
* @param solutions 结果集
* @param queens 填充数组,数组长度为n,下标表示行,元素值表示当前行的位置.有点一维数组当二维数组用的意思
* @param n n个皇后
* @param row 行
* @param columns 列集合
* @param diagonals1 斜线为从左上到右下方向
* @param diagonals2 斜线为从右上到左下方向
*/
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);
} else {
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;
}
public static void main(String[] args) {
//测试4*4的棋盘
List<List<String>> list = new Solution1().solveNQueens(4);
list.forEach(list2->{
list2.forEach(System.out::println);
System.out.println("=====");
});
}
}
方法二:基于位运算的回溯(不推荐)
我们可以用数组来代替集合。
放完了第0行,第1行,那里第2行怎么放呢。
我们用 0 代表可以放置皇后的位置,1 代表不能放置皇后的位置。
很明显,情况如下。
列 , 从左上到右下方向 ,从右上到左下方向
0001 0100 , 0011 0000 ,0000 1001
图片来自leetcode官方
class Solution {
public List<List<String>> solveNQueens(int n) {
int[] queens = new int[n];
Arrays.fill(queens, -1);
List<List<String>> solutions = new ArrayList<List<String>>();
solve(solutions, queens, n, 0, 0, 0, 0);
return solutions;
}
public void solve(List<List<String>> solutions, int[] queens, int n, int row, int columns, int diagonals1, int diagonals2) {
if (row == n) {
List<String> board = generateBoard(queens, n);
solutions.add(board);
} else {
int availablePositions = ((1 << n) - 1) & (~(columns | diagonals1 | diagonals2));
while (availablePositions != 0) {
int position = availablePositions & (-availablePositions);
availablePositions = availablePositions & (availablePositions - 1);
int column = Integer.bitCount(position - 1);
queens[row] = column;
solve(solutions, queens, n, row + 1, columns | position, (diagonals1 | position) << 1, (diagonals2 | position) >> 1);
queens[row] = -1;
}
}
}
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;
}
}
52.n-queens-ii
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.
Given an integer n, return the number of distinct solutions to the n-queens puzzle.
Example:
Input: 4
Output: 2
Explanation: There are two distinct solutions to the 4-queens puzzle as shown below.
[
[".Q..", // Solution 1
"...Q",
"Q...",
"..Q."],
["..Q.", // Solution 2
"Q...",
"...Q",
".Q.."]
]
java代码
class Solution {
public int totalNQueens(int n) {
Set<Integer> columns = new HashSet<Integer>();
Set<Integer> diagonals1 = new HashSet<Integer>();
Set<Integer> diagonals2 = new HashSet<Integer>();
return backtrack(n, 0, columns, diagonals1, diagonals2);
}
public int backtrack(int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
if (row == n) {
return 1;
} else {
int count = 0;
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;
}
columns.add(i);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
count += backtrack(n, row + 1, columns, diagonals1, diagonals2);
columns.remove(i);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
return count;
}
}
}
或者
class Solution {
public int totalNQueens(int n) {
return solve(n, 0, 0, 0, 0);
}
public int solve(int n, int row, int columns, int diagonals1, int diagonals2) {
if (row == n) {
return 1;
} else {
int count = 0;
int availablePositions = ((1 << n) - 1) & (~(columns | diagonals1 | diagonals2));
while (availablePositions != 0) {
int position = availablePositions & (-availablePositions);
availablePositions = availablePositions & (availablePositions - 1);
count += solve(n, row + 1,
columns | position,
(diagonals1 | position) << 1,
(diagonals2 | position) >> 1);
}
return count;
}
}
}
或者
class Solution {
public int totalNQueens(int n) {
int[] rs = new int[]{
0,1,0,0,2,10,4,40,92,352,724,2680};
return rs[n];
}
}