先贴一下题目(来自LeetCode第51题)
因为要求解所有的n皇后放置问题,所以这道题很适合使用dfs,下面先介绍一下dfs:
深度优先搜索(dfs)从它的名字上就很容易理解,它在搜索时对每一个节点都做同一件事情:对其某一分支不断的深入,直到不能再深入为止(到达叶节点),然后退回(回溯)到上一节点(父节点),继续其它的搜索。没错,这看上去就是一个递归过程,那时间复杂度岂不是很高?实则不然,我们知道递归时可以用记忆化搜索法实现动态规划,那么dfs同样可以用减枝(下面介绍)来减少时间复杂度,当然还有A*算法什么的,大家可以百度了解下。
对于dfs最重要的两点判断边界条件和回溯,这里我写了一个伪代码作为dfs的结构框架:
void dfs(int n) {
if(边界条件) {
数据操作代码;
return;
}
for(n的下一节点i) {
if(未被访问) {
标记
dfs(i);
回溯
}
}
当然格式根据题意会有很大的变换,如需要返回值则要修改void,如有多个参数则需要修改入参,如并不需要单独判断边界则去掉前一个if语句,如子节点明确(二叉树时)则去掉for循环,总之要根据实际情况,不能死板套用。
好了,讲了这么多,现在让我们回到题目上来,为方便理解我画了个草图,如下:
这是一个4皇后的示意图,可以看到按照dfs的方法不断搜索最终有256(n^n)种结果,但是我们注意到这里面明显有错误的分支:
通过题意我们可以很容易的去掉两个错误的分支,在接下来的搜索中同样也可以把错误分支去掉,而不是等到最后再来判断是否满足题意,这样就大大减少了计算量,这种方法就称作剪枝(其实就是伪代码中第二个if语句的作用)。
好了,理论搞明白了我们就开始入手做题了:
首先我们来看看如何判断错误分支,根据题意一个皇后位置确定之后,它的行、列、正切线、反切线这四条线上是不能放置皇后的。我们可以用一层for来控制列其他几个条件做成布尔数组,放入if中去判断,当列的索引等于n时,说明到达叶节点,因为已经进行过筛选,此时直接输出即可,代码如下:
import java.util.ArrayList;
import java.util.List;
/**
*
* @author windddy
*
*/
public class LC_51_N皇后 {
List<List<String>> lists;
public List<List<String>> solveNQueens(int n) {
lists = new ArrayList<List<String>>();
//row存储行是否被占用信息
boolean[] row = new boolean[n];
//tan存储正切线是否被占用信息
boolean[] tan = new boolean[2 * n - 1];
//cot存储反切线是否被占用信息
boolean[] cot = new boolean[2 * n - 1];
//q存储皇后位置信息
int[] q = new int[n];
//深度优先遍历
dfs(n, 0, row, tan, cot,q);
return lists;
}
/**
*
* @param n 皇后和棋盘大小
* @param col 当前遍历的列
* @param row 当前已被占用的行,false:未被占用;true:已被占用
* @param tan 当前已被正切的行,false:未被占用;true:已被占用
* @param cot 当前已被反切的行,false:未被占用;true:已被占用
* @param q 当前已记录的皇后位置,
*/
private void dfs(int n, int col, boolean[] row, boolean[] tan, boolean[] cot,int[] q) {
//判断是否已记录到最后一列
if(col==n) {
List<String> list = new ArrayList<String>();
String s;
//将记录值加入list
for (int i = 0; i < n; i++) {
s = "";
for (int j = 0; j < n; j++) {
if(j==q[i]) {
s +="Q";
}else {
s +=".";
}
}
list.add(s);
}
lists.add(list);
}
//遍历当前列的每一个位置
for (int i = 0; i < n; i++) {
//如果该位置已经被标记,就不进入(减枝)
if (!row[i] && !tan[col + i] && !cot[col - i + n - 1]) {
//未被标记进如,记录皇后位置,并标记
q[col] = i;
row[i]= true;
tan[col + i] =true;
cot[col - i + n - 1] = true;
//继续深入
dfs(n,col+1,row,tan,cot,q);
//回溯
row[i]= false;
tan[col + i] =false;
cot[col - i + n - 1] = false;
}
}
}
}
好了,我们OJ一下看看:
只击败了77%,应该还有更好的代码,笔者在这只是抛砖引玉,如有不足之处,恳请大家指正,另外大家也自己去练练吧。
欢迎转载,请注明出处。