LeetCode51 N皇后

19 阅读3分钟

leetcode.cn/problems/n-…

image.png

解法一:回溯

由于每行恰好放一个皇后,我们知道每行必然有且只有一个皇后,所以如果我们决定在 board[i] 这一行的某一列放置皇后,那么接下来就不用管 board[i] 这一行了,应该考虑 board[i+1] 这一行的皇后要放在哪里。 因此 N皇后问题的穷举对象是每行的皇后放在哪一列,然后通过一些题意约束的冲突规则进行剪枝即可。

func solveNQueens(n int) [][]string {
    board := make([][]string, n)
    // 初始化空棋盘
    for i:=0; i<n; i++{
        board[i] = make([]string, n)
        for j:=0; j<n; j++{
            board[i][j] = "."
        }
    }
    var res [][]string
    backtrack(board, 0, &res)
    return res
}

func backtrack(board [][]string, row int, res *[][]string){
    if row == len(board){ // base case, 所有行都放完
        tmp := make([]string, 0)
        for i:=0; i<len(board); i++{
            tmp = append(tmp, strings.Join(board[i], ""))
        }
        *res = append(*res, tmp)
        return
    }
    cols := len(board[0])
    for col := 0; col < cols; col++{
        // 剪枝,跳过非法的位置
        if !isValid(board, row, col) {
            continue
        }
        // 选择这一行的这一列放置皇后
        board[row][col] = "Q"
        // 递归处理下一行
        backtrack(board, row+1, res)
        // 撤销选择
        board[row][col] = "." 
    }
}

// 因为我们是从上往下放置皇后的,所以只需要检查上方是否有皇后互相冲突
func isValid(board [][]string, row, col int) bool{
    // 检查同列的上方是否已放置其它皇后
    for i:=row-1; i>=0;i--{
        if board[i][col] == "Q"{
            return false
        }
    }
    // 检查左、右上方对角线上是否放置其它皇后
    for i:=row-1; i>=0; i--{
        digCol := col - (row-i)
        // 左上方
        if digCol >= 0 && board[i][digCol] == "Q"{
            return false
        }
        // 右上方
        digCol = col + (row-i)
        if digCol < len(board[0]) && board[i][digCol] == "Q"{
            return false
        }
    }
    return true
}

遍历左右对角线的for循环可以写成同步更新行和列的for循环,另外由于结果要把一行的所有列合并表示为一个string,我们可以对每一列用rune字符表示

func solveNQueens(n int) [][]string {
    board := make([]string, n)
    // 初始化空棋盘
    for i:=0; i<n; i++{
        chars := make([]rune, n)
        for j:=0; j<n; j++{
            chars[j] = '.'
        }
        board[i] = string(chars)
    }
    var res [][]string
    backtrack(board, 0, &res)
    return res
}

func backtrack(board []string, row int, res *[][]string){
    if row == len(board){ // base case, 所有行都放完
        tmp := make([]string, len(board))
        copy(tmp, board)
        *res = append(*res, tmp)
        return
    }
    cols := len(board[0])
    for col := 0; col < cols; col++{
        // 剪枝,跳过非法的位置
        if !isValid(board, row, col) {
            continue
        }
        // 选择这一行的这一列放置皇后
        rowChars := []rune(board[row])
        rowChars[col] = 'Q'
        board[row] = string(rowChars)
        // 递归处理下一行
        backtrack(board, row+1, res)
        // 撤销选择
        rowChars[col] = '.'
        board[row] = string(rowChars)
    }
}

// 因为我们是从上往下放置皇后的,所以只需要检查上方是否有皇后互相冲突
// board一维是[]string,二维是[]rune
func isValid(board []string, row, col int) bool{
    // 检查同列的上方是否已放置其它皇后
    for i:=row-1; i>=0;i--{
        if board[i][col] == 'Q'{ 
            return false
        }
    }
    // 检查左上方对角线上是否放置其它皇后
    for i, j := row-1, col-1; i>=0 && j>=0; i,j = i-1, j-1{
        if board[i][j] == 'Q'{
            return false
        }
    }
    // 检查右上方对角线上是否放置其它皇后
    for i, j := row-1, col+1; i>=0 && j<len(board[0]); i,j = i-1, j+1{
        if board[i][j] == 'Q'{
            return false
        }
    }
    return true
}