n皇后问题是将n个皇后放置在n*n的棋盘上,皇后彼此之间不能相互攻击。
给定一个整数n,返回所有不同的n皇后问题的解决方案。
每个解决方案包含一个明确的n皇后放置布局,其中”Q”和”.”分别表示一个女王和一个空位置。
样例
对于4皇后问题存在两种解决的方案:
[
[“.Q..”, // Solution 1
“…Q”,
“Q…”,
“..Q.”],
[“..Q.”, // Solution 2
“Q…”,
“…Q”,
“.Q..”]
]
挑战
你能否不使用递归完成?
标签
递归 深度优先搜索
相关题目
中等 Pacific Atlantic Water Flow27 %
中等 组合33 %
中等 N皇后问题 II41 %
(1)C++
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
const int N = 4;// N皇后
class NQueens{
public:
vector<vector<string>> solveNQueens(int n){
vector<vector<string>> result;
vector<vector<int>> mark;// 标记皇后的攻击位置(标记数组)
vector<string> location;//存储某个摆放皇后的结果,当完成一次递归找到后,将其放入result
for(int i = 0; i < n; i++){// 初始化标记数组
mark.push_back(vector<int>());
for(int j = 0; j < n; j++){
mark[i].push_back(0);
}
location.push_back("");// C++中的字符串处理
location[i].append(n, '.');// 初始化location:每一行(字符串)都追加n个'.'(表示空位,后续若放置了皇后将变成'Q')
}
helper(0, n, location, result, mark);
return result;
}
private:
// 第x行y列放置皇后(置1),mark为一个棋盘
void put_down_the_queen(int x, int y,
vector<vector<int>>& mark){
static const int dx[] = {-1, 1, 0, 0, -1, -1, 1, 1};// 方向数组
static const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1};// (x,y)对应看
int directions = sizeof(dx) / sizeof(int); // directions = 8;
mark[x][y] = 1;
for(int i = 1; i < mark.size(); i++){
for(int j = 0; j < directions; j++){// 共8个方向(四面八方),每个方向 向外延伸1至N-1
int new_x = x + i * dx[j];//dx[]、dy[]表方向; i表延伸长度。
int new_y = y + i * dy[j];// x、y表当前位置
if(new_x >= 0 && new_x < mark.size() &&
new_y >= 0 && new_y < mark.size()){
mark[new_x][new_y] = 1;// 将延伸方向上的所有坐标置1.(被标记处不可以再放置皇后Q)
}
}
}
}
void helper(int row, int nRows,// nRows:N皇后,共nRows行。 row:代表完成了row个皇后的放置,正在放置第row行皇后
vector<string> &location,
vector<vector<string>>& result,
vector<vector<int>>& mark){
if(row == nRows){// 说明一次递归完成,找到一个可行解
result.push_back(location);
return;
}
for(int j = 0; j < nRows; j++){// 按列优先的顺序尝试(在每一行row的遍历中)
if(mark[row][j] == 0){// 若当前位置尚未被标记,即可能是皇后Q的合法位置
vector<vector<int>> tmpMark = mark;//记录回溯前mark的镜像
location[row][j] = 'Q';//放置皇后
put_down_the_queen(row, j, mark);// 放置皇后, 并更新mark标记数组
helper(row + 1, nRows, location, result, mark);// 递归下一行皇后位置
mark = tmpMark;//回溯后要怎么做? ——①将mark重新赋值为回溯前的状态
location[row][j] = '.';// ②将当前尝试的皇后位置重新置空('.')
}
}
}
};
int main()
{
vector<vector<string>> result;
NQueens nQueens;
result = nQueens.solveNQueens(N);
for(int i = 0; i < result.size(); i++){
cout << "i = " << i << endl;
for(int j = 0; j < result[i].size(); j++){
cout << result[i][j].c_str() << endl;
}
cout << endl;
}
return 0;
}
(2)Java
package Recursion;
import java.util.ArrayList;
import java.util.List;
public class N_Queens {
/**
* Get all distinct N-Queen solutions
* @param n: The number of queens
* @return: All distinct solutions
* For example, A string '...Q' shows a queen on forth position
*/
public List<List<String>> solveNQueens(int n){
List<List<String>> results = new ArrayList<>();
if(n <= 0){
return results;
}
search(results, new ArrayList<>(), n);
return results;
}
/**
* results store all of the chessboards
* ❤️ mark store the column indices for each row
* ❤️ mark中存储的是每一行中有且仅有的唯一一个皇后Q的下标!!!
*
* @param mark 按列优先的顺序搜索,并记录每行Q的列下标
* @param nRows 总行数
*/
private void search(List<List<String>> results,
List<Integer> mark,
int nRows){
if(mark.size() == nRows){
results.add(drawChessboard(mark));
return;
}
for (int colIndex = 0; colIndex < nRows; colIndex++) {
if (!isValid(mark, colIndex)) {
continue;
}
mark.add(colIndex);
search(results, mark, nRows);
mark.remove(mark.size() - 1); //mark.remove( mark.indexOf(colIndex) );
}
}
private List<String> drawChessboard(List<Integer> mark){
List<String> chessboard = new ArrayList<>();
for(int i = 0; i < mark.size(); i++){
StringBuilder sb = new StringBuilder();
for(int j = 0; j < mark.size(); j++){
sb.append(j == mark.get(i)? 'Q' : '.');
}
chessboard.add(sb.toString());
}
return chessboard;
}
/**
*
* @param mark
* @param colIndex 当前遍历的列。
* ❤️ mark.get(rowIndex):表示第rowIndex行的皇后Q的列下标
* @return
*/
private boolean isValid(List<Integer> mark, int colIndex){
int nRows = mark.size();
for(int rowIndex = 0; rowIndex < mark.size(); rowIndex++){
if(mark.get(rowIndex) == colIndex){
// mark中rowIndex行的Q位置(列下标)与当前遍历的colIndex相同
return false;
}
if(rowIndex + mark.get(rowIndex) == nRows + colIndex){
//❤️ 当前行的皇后Q的坐标x + y之和与其【副对角线】的坐标和相同
// 排除当前行的皇后Q的【副对角线】上的点
return false;
}
if (rowIndex - mark.get(rowIndex) == nRows - colIndex) {
//❤️ 当前行的皇后Q的坐标y - x之差与其【主对角线】的坐标差相同
// 排除当前行的皇后Q的【主对角线】上的点
return false;
}
}
return true;
}
public static void main(String[] args){
N_Queens n_queens = new N_Queens();
List<List<String>> results = new ArrayList<>();
results = n_queens.solveNQueens(4);
for(int i = 0; i < results.size(); i++){
System.out.println("i = " + i);
System.out.println(results.get(i).toString().replace(',','\n') + "\n");
}
}
}