开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第12天,点击查看活动详情。
N皇后
描述
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击(任意两个皇后都不能处于同一行、同一列或同一斜线上)。给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
难度
困难
示例
输入: 4
输出: [
[".Q..", // 解法 1
"...Q",
"Q...",
"..Q."],
["..Q.", // 解法 2
"Q...",
"...Q",
".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。
思路
使用回溯法。先将第一个皇后摆放在第一行第一列,接着将第二个皇后先摆在第二行第一列,如果和之前摆放过的皇后冲突再摆放在第二行第二列,还是冲突继续在摆放在下一列,依次类推,整个过程递归进行,当最后一个皇后摆放完成后,回溯到上一个皇后,继续摆放。
用一个一维数组 positions 存储象棋摆放的位置,下标表示第几个象棋,数组的值表示第几列。将当前皇后和之前摆放过的皇后依次比较,摆放第 n 个皇后时,positions[n] == positions[i] 表示在同一列, |n - i| == |positions[n] - positions[i]| 表示在同一斜线。
每次摆放完成后构建出一个描述棋盘的数组。
代码
Rust
pub struct Queen {
pub positions: Vec<i32>, // 记录每一次摆放的位置。下标表示第几个皇后,对应的值表示第几列
pub outputs: Vec<Vec<String>>, // 保存输出的结果
}
impl Queen {
pub fn new(n: u32) -> Self {
Queen {
positions: vec![0; n as usize],
outputs: vec![],
}
}
/// 放置皇后
pub fn put_queen(&mut self, n: u32) {
// 最后一个皇后摆放完成
if n as usize == self.positions.len() {
// 打印出摆放的位置
// println!("{:?}", self.positions);
// 根据摆放的位置创建数组
self.add_solution(self.positions.clone());
return;
}
// 有几个位置就有几个皇后
for i in 0..self.positions.len() {
// i = 0 时,假设当前皇后可以放在第一列
// 如果不能放,将进行下一次循环,当前皇后放在下一个位置
self.positions[n as usize] = i as i32;
if self.is_not_conflict(n as usize) {
// 继续摆放下一皇后
self.put_queen(n + 1);
}
}
}
// 判断当前皇后是否和已经摆放过的皇后冲突
pub fn is_not_conflict(&self, n: usize) -> bool {
for i in 0..n {
// positions[n] == positions[i] 表示在同一列
// abs((n - i)) == abs((positions[n] - positions[i]) 表示同一斜线
if self.positions[n] == self.positions[i] ||
(n as i32 - i as i32).abs() == (self.positions[n] - self.positions[i]).abs() {
return false;
}
}
true
}
fn add_solution(&mut self, positions: Vec<i32>) {
let length = positions.len();
let mut output: Vec<String> = vec!["".to_string(); length];
for i in 0..length {
let mut string = String::new();
for _j in 0..positions[i] {
string.push('.');
}
string.push('Q');
for _j in positions[i] + 1..length as i32 {
string.push('.');
}
output[i] = string;
}
self.outputs.push(output);
}
}
pub struct Solution {}
impl Solution {
pub fn solve_n_queens(n: i32) -> Vec<Vec<String>> {
let mut queen = Queen::new(n as u32);
queen.put_queen(0);
queen.outputs
}
}
#[test]
fn test_solve_n_queens() {
let n = 4;
let outputs = Solution::solve_n_queens(n);
println!("{}", n);
println!("{:?}", outputs);
}
运行结果:
4
[[".Q..", "...Q", "Q...", "..Q."], ["..Q.", "Q...", "...Q", ".Q.."]]
Go
type Queen struct {
positions []int // 记录每一次摆放的位置。下标表示第几个皇后,对应的值表示第几列
Outputs [][]string // 保存输出的结果
}
func NewQueen(n int) *Queen {
positions := make([]int, n)
return &Queen{positions: positions, Outputs: nil}
}
// 放置皇后
func (queen *Queen) PutQueen(n int) {
// 最后一个皇后摆放完成
if n == len(queen.positions) {
// 打印出摆放的位置
// fmt.Printf("%v", queen.positions)
// 根据摆放的位置创建数组
queen.addSolution(queen.positions)
return
}
// 有几个位置就有几个皇后
for i := 0; i < len(queen.positions); i++ {
// i = 0 时,假设当前皇后可以放在第一列
// 如果不能放,将进行下一次循环,当前皇后放在下一个位置
queen.positions[n] = i
if queen.isNotConflict(n) {
// 继续摆放下一皇后
queen.PutQueen(n + 1)
}
}
}
// 判断当前皇后是否和已经摆放过的皇后冲突
func (queen *Queen) isNotConflict(n int) bool {
for i := 0; i < n; i++ {
// positions[n] == positions[i] 表示在同一列
// math.Abs(float64(n - i)) == math.Abs(float64(positions[n] - positions[i]) 表示同一斜线
if queen.positions[n] == queen.positions[i] ||
math.Abs(float64(n - i)) == math.Abs(float64(queen.positions[n] - queen.positions[i])) {
return false
}
}
return true
}
func (queen *Queen) addSolution(positions []int) {
length := len(positions)
output := make([]string, length)
for i := 0; i < length; i++ {
var stringBuilder strings.Builder
for j := 0; j < positions[i]; j++ {
stringBuilder.WriteString(".")
}
stringBuilder.WriteString("Q")
for j := positions[i] + 1; j < length; j++ {
stringBuilder.WriteString(".")
}
output[i] = stringBuilder.String()
}
queen.Outputs = append(queen.Outputs, output)
}
func solveNQueens(n int) [][]string {
queen := NewQueen(n)
queen.PutQueen(0)
return queen.Outputs
}
func TestSolveNQueens(t *testing.T) {
n := 4
outputs := solveNQueens(n)
t.Log(n)
t.Logf("%v\n", outputs)
}
运行结果:
4
[[.Q.. ...Q Q... ..Q.] [..Q. Q... ...Q .Q..]]
Java
public class NQueen {
private int[] positions; // 记录每一次摆放的位置。下标表示第几个皇后,对应的值表示第几列
private List<List<String>> outputs; // 保存输出的结果
public void putQueen(int n) {
// 最后一个皇后摆放完成
if (n == this.positions.length) {
// 打印出摆放的位置
// System.out.println(Arrays.toString(this.positions));
// 根据摆放的位置创建数组
this.addSolution(this.positions);
return;
}
// 有几个位置就有几个皇后
for (int i = 0; i < this.positions.length; i++) {
// i = 0 时,假设当前皇后可以放在第一列
// 如果不能放,将进行下一次循环,当前皇后放在下一个位置
this.positions[n] = i;
if (this.isNotConflict(n)) {
// 继续摆放下一皇后
this.putQueen(n + 1);
}
}
}
/**
* 判断当前皇后是否和已经摆放过的皇后冲突
*/
private boolean isNotConflict(int n) {
for (int i = 0; i < n; i++) {
// this.positions[n] == this.positions[i] 表示在同一列
// Math.abs(n - i) == Math.abs(this.positions[n] - this.positions[i]) 表示同一斜线
if (this.positions[n] == this.positions[i] ||
Math.abs(n - i) == Math.abs(this.positions[n] - this.positions[i])) {
return false;
}
}
return true;
}
private void addSolution(int[] positions) {
int length = positions.length;
List<String> output = new ArrayList<>();
for (int position : positions) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < position; j++) {
sb.append(".");
}
sb.append("Q");
for (int j = position + 1; j < length; j++) {
sb.append(".");
}
output.add(sb.toString());
}
this.outputs.add(output);
}
public List<List<String>> solveNQueens(int n) {
this.positions = new int[n];
this.outputs = new ArrayList<>();
this.putQueen(0);
return this.outputs;
}
public static void main(String[] args) {
int n = 4;
NQueen queen = new NQueen();
List<List<String>> outputs = queen.solveNQueens(n);
System.out.println(n);
outputs.forEach(output -> System.out.println(Arrays.toString(output.toArray())));
}
}
运行结果:
4
[.Q.., ...Q, Q..., ..Q.]
[..Q., Q..., ...Q, .Q..]