LeetCode刷题(python版)——Topic51N 皇后

一、题设

按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。

n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。

每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

二、基本思路

        N皇后这种类似的棋盘问题,回溯即可:

        首先我们先写回溯算法,回溯的模板如下:

        def backtracking(参数):
    if 终止条件: # 达到叶子节点
        收集数据
        return
    # 单层逻辑
    for i  in range(lens):#遍历数据集
        处理节点
        backtracking(参数)
        撤销处理节点的操作

        (1)于是我们先确定终止条件:当填入n个皇后了就是产生了一个结果,将它收集.故终止条件为:if 某一回溯参数 == n(皇后个数) . 则res.append(...) return 

        (2)第二步,我们要确定单层逻辑:起初我想的是两层循环遍历每一个数组,这样写时间复杂度就高了,而且可能回不了。于是最核心的一个思路出来了,就是用一个全局的数组去记录每行的皇后所在位置,比如说flag[i] = j ,就代表第i行的皇后在第j列,其他没有皇后的位置就是'.',也可以暂时标记为-1.有个这个思想,我们就不用循环行了,而且还可以把行数当作回溯算法的一个参数传进来,这样不仅解决了终止条件,还减少了循环趟数。由此,我们循环第i行(i为传入参数)的每列,通过isValid()函数判断当前的(i,j)位置是否可以填入;若可填入则修改全局数组的值标记皇后位置,然后进行回溯.回溯之后的撤销操作即将皇后位置抹去。

        (3)第三步是去写isValid()函数:我们先从[0,row)遍历之前的皇后,如果flag[i] == col(有已标记皇后和当前皇后在一列),或者flag[i] + (row - i) == col(有已标记皇后和当前皇后在主对角线),或者flag[i] - (row - i) == col(有已标记皇后和当前皇后在副对角线),在同一行不用判断,因为我只循环列即一行填一次。

        那么关键就在于主副对角线上的判断条件怎么写,元素在主副对角线上即为横纵坐标差之比为1(主对角线),为-1(副对角线),则有:

                       

          (figure1:主对角线关系)                                             (figure2:副对角线关系)

 于是就有:

if flag[i] == col or flag[i] + (row-i) == col or flag[i] - (row - i) == col:
                    return False

        (4)第四步就是将存储好的一个数字列表转化成字符串啦,在这里就是用generateString()这一函数实现的.就是将全局标记数组中-1值的位置转化为'.',有非-1值得地方转化为'Q'即可。这里要注意的是,我起初想直接这样赋值:

 for row in range(n):
 s = ""
 for j in range(n):
     if j == flag[row]:
         s += 'Q'
     else:
         s += '.'   

        以上是没毛病的,但是我在看评论的时候我看到了另一种赋值的方法,即第一遍全部填'.',第二遍直接利用索引填'Q',即这样:

for row in range(n):
    s = ""
    for j in range(n):
        s += '.'  
    s[flag[row]] = 'Q' 

         直接用索引就会产生报错, 故还是直接用第一种方法即可.python中的s = ""只是生成了一个可迭代的字符容器罢了,具体的大小也没有确定,所以在使用索引时就会报错;而不是像Java中的这条语句,开辟了一段固定大小的空间。故python写法的用第一种方法遍历判断即可。

char[] eachRow = new char[n]

三、代码实现


    def solveNQueens(self, n):
        res = [] # 用于记录最后结果
        # flag记录当前行的状态
        # flag[i] = j 表示第i行的皇后在j列
        flag = [-1 for _ in range(n)]
        # generateString函数用于结合flag中的皇后位置生成一个结果
        def generateString(n):
            current_res = []
            for row in range(n):
                s = ""
                for j in range(n):
                    if j == flag[row]:
                        s += 'Q'
                    else:
                        s += '.'  
                current_res.append(s)
            return current_res
        # 判断当前(row,col)能否填入
        def isValid(row,col):
            current_res = True
            # 遍历前几行
            for i in range(row):
                if flag[i] == -1:
                    continue
                if flag[i] == col or flag[i] + (row-i) == col or flag[i] - (row - i) == col:
                    return False
            return current_res
        def backtracing(n,row):
            if row == n:
                res.append(generateString(n))
                return
            for col in range(n):# 循环每一列
                # 当前位置不合法
                if not isValid(row,col):
                    continue
                # 合法,可填入
                flag[row] = col
                backtracing(n,row + 1)
                flag[row] = -1

        backtracing(n,0)
        return res

四、效率总结

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值