n皇后问题 回溯法_N皇后问题II

本文介绍了N皇后问题的回溯法解决方案,通过确保每次加入的新皇后在其限制路径中不会与其他皇后冲突,将多点限制问题转化为单点限制问题。文中提到的Solution1在加入每个皇后时,检查其左上、左下和左边路径,总时间复杂度为O(n^3),空间复杂度为O(n)。
摘要由CSDN通过智能技术生成
09ed1dee154942b23e7cb070e507f487.png

有两个月没刷算法题了,之前已经刷了前150,感觉还不太熟6a29f47b1592a62d13954d18eade34e0.png。现重刷1遍,写写解法,加深印象。Queen问题还算比较经典的算法题,面试常考,它有非常多的解法,最后一个还挺巧妙的:利用二进制作为滚动数组,非常喜欢!(鸣谢小光师兄对博客内容的提醒⏰)

N皇后问题II来自Leetcode:52.N-QueenII

皇后,是国际象棋中的棋子,意味着国王的妻子。皇后只做一件事,那就是“吃子”。当她遇见可以吃的棋子时,就迅速冲上去吃掉棋子。当然,她横、竖、斜都可走一或 N-1 步,可进可退。(百度百科)

原题如下?
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.Given an integer n, return the number of distinct solutions to the n-queens puzzle.Example:Input: 4Output: 2Explanation: There are two distinct solutions to the 4-queens puzzle as shown below.
[
[".Q..",  // Solution 1  "...Q",    "Q...",    "..Q."],
["..Q.",  // Solution 2  "Q...",    "...Q",    ".Q.."]]

一开始,我在做这道题的时候思路有点乱,n个皇后,每一个皇后都会有自己的路径限制。加入一个皇后,就要测试在新皇后在不在已经加入的皇后的限制路径内。这个思想不太可行,特乱!那我们就想,如果只要保证每次加入新皇后后,在它的限制路径里没有其他已经加入的皇后,那就可以保证所有的皇后都不会冲突了,一个多点限制问题就变成了单个点的限制问题。于是就有了Solution1。

fd32e32d6e267cfe061b9d5bfdc6e9db.png

待会要用到的参数x,y如上图?

# Solution1:校验法+回溯法

Solution1最核心的逻辑就在于Check操作如何保证限制路径里没有其他已经加入的皇后。思路就是只要判断新加入的皇后的左上路径upperLeftTrack和左下路径bottomLeftTrack和左边直射路径y没有皇后即可,确认过程中需要O(n)的复杂度,从x-1遍历起直到0。测试n行,也是O(n)。因为有不同解法,必定要用到回溯O(n),总体是O(n^3)的时间复杂度,O(n)的空间复杂度。总体不算太美!但是还是可以直接想到的解法,不至于慌得一批。

## Pythonclass Solution(object):    def totalNQueens(self, n):        resLen = []        def dfs(stack,x):            if x==n:                resLen.append(1)                return             for y in range(n):                if check(stack,x,y):                    stack.append(y)                    dfs(stack,x+1)                    stack.pop()         def check(stack,x,y):            ## 确认(x,y)的轨迹上不会有其他的皇后            upperLeftTrack = y-1            bottomLeftTrack = y+1            for i in range(x-1,-1,-1):                if y == stack[i]: return False                if upperLeftTrack == stack[i]:  return False                if bottomLeftTrack == stack[i]: return False                upperLeftTrack -= 1                bottomLeftTrack += 1            return True         dfs([],0)        return len(resLen)

其实除了上面的起两个变量upperLeftTrack和bottomLeftTrack来模拟测试点的斜线路径外,还有一种方法来验证其他皇后在不在测试点(x,y)的斜线范围,就是使用x-y和x+y来测试。如果其他皇后的坐标x-y或x+y和测试点的一样,那么它们就在同条斜线上,此时我们应该抛弃这个点,Solution2就是采用的这个做法?

# Solution2:交叉线限制+回溯法

在下面的解法中subResult用来存放[已经测试过的皇后的y值],如何得到x值呢?靠数组的坐标。Solution2和Solution1的区别就只是在Check的阶段而已,总体是O(n^3)的时间复杂度,O(n)的空间复杂度。依然不美!

// JavaScriptvar totalNQueens = function(n) {    let result = 0    const dfs = (subResult=[],col=0)=>{        if (col===n){            result++            return        }        for (let row=0;row            if (subResult.some((y,x)=> y===row || x-y===col-row || x+y===col+row )) continue            subResult.push(row)            dfs(subResult,col+1)            subResult.pop()        }    }    dfs()    return result};

Solution2和Solution1,确实解决了问题,但在时间上太不美了,怎么办?那就空间换时间吧!我们就不一一测试其他皇后在不在测试皇后的斜线方向上是否占位。我们就把过去的皇后的y值,x+y值,x-y值作为键Key存储在r,s2,s1数组里,value值1就表示已经存在,value值0就表示不存在。这样一来check操作O(1)的时间复杂度就可以搞得定了。时间复杂度就降为了O(n^2),空间复杂度还是O(n)。

# Solution3:时间换空间的标记法+回溯法

需要注意的是不过x-y要进行一定数组坐标移位,因为x-y有可能是负数(下面的depth-i其实就是x-y,+n-1就是给它变正)。其实步骤和Solution2没啥不同,就是改变了一下检验的操作!

## Pythonclass Solution(object):    def totalNQueens(self, n):        s1 = [0 for i in range(2*n-1)]        s2 = [0 for i in range(2*n-1)]        r = [0 for i in range(n)]        List = []        def dfs(depth,r,s1,s2):            if depth == n:                List.append(1)                return             for i in range(n):                if s1[depth-i+n-1]==1  or s2[depth+i]==1 or r[i] == 1: continue                s1[depth-i+n-1],s2[depth+i],r[i] = 1,1,1                dfs(depth+1,r,s1,s2)                s1[depth-i+n-1],s2[depth+i],r[i] = 0,0,0        dfs(0,r,s1,s2)               return len(List)

有了Solution3其实我们就可以想,数组解法的话,空间就增加了。那有什么可以代替数组来存储0和1呢?我就会想到二进制的位运算,就可以代替数组完成工作,我们叫滚动数组。Solution3中的数组画出来,不就是1001101这些数字吗?

我和大家一样其实都挺怕二进制的,但后来觉得把基本的现象理解了,自然就不陌生了。

面?是基本的现象

## 与操作 如果两个数,位数上有一位相同则整体不为0,位数上都不相同的话直接整体为0。与操作可以用来查看测试皇后是否占到了其他皇后的位,占到了就不为0了

print(12 & 4) # 0

--> 1100 & 0010 = 0000

print(12 & 2) # 4

--> 1100 & 0100 = 0100

## 或操作 位数上只要有1个数有1,就写1。或操作可以把测试皇后的位置加到s1,s2,r二进制数组中。

print(12 | 4) # 14

--> 1100 | 0010 = 1110

# Solution4:交叉线限制+位运算+回溯法,

总体还是O(n^2)的复杂度,空间复杂度达到O(1)。Solution4实际上也只是改变了一种Check操作而已,没啥不一样的!

## Pythonclass Solution(object):    def totalNQueens(self, n):        List = []        def dfs(depth,r,s1,s2):            if depth == n:                List.append(1)                return            for i in range(n):                j = 1<                if (r&j)|(s1&j)|(s2&j):                     continue                dfs(depth+1,r|j,(s1|j)<<1,(s2|j)>>1)        dfs(0,0,0,0)        return len(List)

提示?

## 1< 

移位到皇后测试点上。

print(1<<0) ## 0001

print(1<<2) ## 0100

## (r&j)|(s1&j)|(s2&j)

检测该点在不在过去皇后的记录里,row行,上斜线s1,下斜线s2中。

## r|j,(s1|j)<<1,(s2|j)>>1 

把j点加入到原来的数组中,记得s1和s2要进行滚动,这是因为斜线要下移的关系,数组也要跟着下移动,用纸画一下就懂。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值