【LeetCode】 N皇后

86 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第12天,点击查看活动详情

N皇后

原题:leetcode-cn.com/problems/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..]