N皇后-Kotlin解法

LeetCode 51.N皇后
简单解释一下:给你一个 N×N 的棋盘,让你放置 N 个皇后,使得它们不能互相攻击。
PS:皇后可以攻击同一行、同一列、左上左下右上右下四个方向的任意单位。

Kotlin解法如下,先上代码:

class Solution {
    /**
     * 无重叠子问题,只能用回溯法,暴力穷举
     * 指数级时间复杂度
     */
    private var result: MutableList<MutableList<String>> = mutableListOf()
    fun solveNQueens(n: Int): MutableList<MutableList<String>> {
        var board: MutableList<MutableList<String>> = MutableList(n) { MutableList(n) { "." } }
        backtrack(board, 0)
        return result;
    }

    /**
     * 回溯函数
     */
    private fun backtrack(board: MutableList<MutableList<String>>, row: Int) {
        //满足条件,每一行都放置了皇后、记录结果
        if (row == board.size) {
            result.add(helper(board))
            return
        }

        //在当前行的每一列都可以放置皇后
        for (column: Int in 0 until board.size) {
            //排除可以相互攻击的格子
            if (!isValid(board, row, column)) {
                continue
            }
            //做选择
            board[row][column] = "Q"
            //进入下一行放皇后
            backtrack(board, row + 1)
            //撤销选择
            board[row][column] = "."
        }

    }

    /**
     * 判断位置(row , column)是否可以放置皇后
     */
    private fun isValid(board: MutableList<MutableList<String>>, row: Int, column: Int): Boolean {

        //检查左上方
        var i = row - 1
        var j = column - 1

        while (i >= 0 && j >= 0) {
            if (board[i][j] == "Q") {
                return false
            }
            i--
            j--
        }

        //检查正上方
        i = row - 1
        j = column
        while (i >= 0) {
            if (board[i][j] == "Q") {
                return false
            }
            i--
        }

        //检查右上方
        i = row - 1
        j = column + 1
        while (i >= 0 && j < board.size) {
            if (board[i][j] == "Q") {
                return false
            }
            i--
            j++
        }

        return true
    }

    /**
     * 数据结构转换函数,
     * 将MutableList<MutableList<String>>)转换为 MutableList<String>
     */
    private inline fun helper(board: MutableList<MutableList<String>>): MutableList<String> {
        var tempList: MutableList<String> = mutableListOf()
        var strB = StringBuilder()
        for (i: Int in 0 until board.size) {

            for (j: Int in 0 until board[i].size) {
                strB.append(board[i][j])
            }
            tempList.add(strB.toString())
            strB.clear()
        }
        return tempList
    }

以n=2,即2*2棋盘为例。

 

决策树的每一层表示棋盘上的每一行;每个节点可以做出的选择是,在该行的任意一列放置一个皇后。

理解算法可能产生的疑问:

问题1:按照 N 皇后问题的描述,为什么不检查左下角,右下角和下方的格子,只检查了左上角,右上角和上方的格子?

因为皇后是一行一行从上往下放的,所以左下方,右下方和正下方不用检查(还没放皇后);因为一行只会放一个皇后,所以每行不用检查。也就是最后只用检查上面,左上,右上三个方向。

回溯算法就是个多叉树的遍历问题,N皇后问题,没有重叠子问题,只能用回溯算法穷举。
动态规划的暴力求解阶段就是回溯算法。只是有的问题具有重叠子问题性质,可以用 dp table或者备忘录优化,将递归树大幅剪枝,这就变成了动态规划。

N皇后问题的时间复杂度非常高、指数级,最坏时间复杂度是 O(N^(N+1))。

问题2:时间复杂度太高怎么办?

找所有解法复杂度太高,比如解数独的算法,只要找到一种解法就可以。

    /**
     * 回溯函数
     */
    private fun backtrack(board: MutableList<MutableList<String>>, row: Int): Boolean {
        //满足条件,每一行都放置了皇后、记录结果
        if (row == board.size) {
            result.add(helper(board))
           //只要找到一个答案,就返回
            return true
        }

        //在当前行的每一列都可以放置皇后
        for (column: Int in 0 until board.size) {
            //排除可以相互攻击的格子
            if (!isValid(board, row, column)) {
                continue
            }
            //做选择
            board[row][column] = "Q"
            //进入下一行放皇后
            backtrack(board, row + 1)
            //撤销选择
            board[row][column] = "."
        }
        return false
    }

只要找到一个答案,for 循环的后续递归穷举都会被阻断。

猜你喜欢

转载自blog.csdn.net/lrxb_123/article/details/123904308