![09ed1dee154942b23e7cb070e507f487.png](https://i-blog.csdnimg.cn/blog_migrate/a6ea8d413277e1e5143b6253bc65390c.png)
有两个月没刷算法题了,之前已经刷了前150,感觉还不太熟。现重刷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。
待会要用到的参数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)的空间复杂度。依然不美!
// JavaScript
var 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没啥不同,就是改变了一下检验的操作!
## Python
class 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操作而已,没啥不一样的!
## Python
class 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要进行滚动,这是因为斜线要下移的关系,数组也要跟着下移动,用纸画一下就懂。