一、题目描述:
leetcode 52.N-Queens II: n皇后问题,要求返回指定格式的二维列表,其中“Q”表示皇后的位置,“.”表示皇后不在的位置。
二、思路分析:
1.类别
递归、回溯、位运算、动态规划
2.普通递归思路
在的格子中摆N个皇后,第i个皇后就摆在i行,可以用一个一维数组表示record [i] 表示第i行的存储皇后的位置(列数)。如果用普通的递归方法,简单的代码如下:
设置一个循环的变量i,i相当于行指针,当i到达最后一行时,返回1,对每一列都进行尝试,如果isValid函数为真,那么j能够被放置。累加调用递归函数尝试放置下一行的结果,这样就会把所有的情况都累加起来了。
isValid()函数会讨论两种情况,一种是共列,一种是在对角线上。当两个皇后共对角线时,它们行的差值等于列的差值。
int process1(int i, int* &record int n){
if(i==n)
return 1;
for(int j=0;j<n;j++) //尝试每一列
{
if(isValid(record,i,j)){
record[i]=j;
res+=process1(i+1,record,n);
}
}
}
bool isValid(int* &record,int i, int j){
//共行 c==d
//共斜线abs(a-c)==abs(b-d);
}
3.位运算解法
位运算的解法在leetcode 51.N-Queens【递归、位运算】【hard】中有比较详细的解释。如果做了51再做52的话,其实只要把返回值从二维数组改为返回二维数组的大小即可。具体代码如下,尝试后其实速度并不慢,但是占用的空间较大。
class Solution {
public:
void reculSolution(int n, int limit, int lLim, int rLim, int colLim,
vector<int> &pos, vector<vector<int>> &result)
{
int avaPos = limit & ~(colLim | lLim | rLim);
int mostRight;
while (avaPos != 0)
{
mostRight = avaPos & (~avaPos + 1);
avaPos = avaPos - mostRight;
pos.push_back(mostRight);
reculSolution(n, limit, (lLim | mostRight) << 1, (unsigned int)(rLim | mostRight) >> 1,
colLim | mostRight, pos, result);
if (pos.size() == n)
result.push_back(pos);
pos.pop_back();
}
}
int totalNQueens(int n) {
int limit = n == 32 ? -1 : (1 << n) - 1;
int lLim = 0, rLim = 0, colLim = 0; //左对角线限制、右对角线限制、列限制
vector<vector<int>> result;
vector<int> pos;
reculSolution(n, limit, lLim, rLim, colLim, pos, result);
return result.size();
}
};
因此可以考虑将原来的递归代码进行一些修改,结合普通递归的思路,函数的返回值为int, 累加调用函数后的返回值。具体代码在第三部分,能够明显地看到空间复杂度降低了。
三、AC 代码:
class Solution {
public:
int reculSolution(int limit, int lLim, int rLim, int colLim)
{
int result=0;
if(colLim==limit)
return 1;
int avaPos = limit & ~(colLim | lLim | rLim);
int mostRight;
while (avaPos != 0)
{
mostRight = avaPos & (~avaPos + 1);
avaPos = avaPos - mostRight;
result+=reculSolution(limit, (lLim | mostRight) << 1,
(unsigned int)(rLim | mostRight) >> 1,
colLim | mostRight);
}
return result;
}
int totalNQueens(int n) {
int limit = n == 32 ? -1 : (1 << n) - 1;
int lLim = 0, rLim = 0, colLim = 0; //左对角线限制、右对角线限制、列限制
int result=reculSolution(limit, lLim, rLim, colLim);
return result;
}
};
四、总结
这一题和leetcode 51.N-Queens【递归、位运算】【hard】能够放在一起对比,也是因为从暴力递归到位运算的这一思路值得借鉴和思考。并且从递归还能够直接转化成动态规划来做,后续再填坑吧。
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情