或许很多读者尚未发现,八皇后问题存在一个重要数学规律。如何运用这个规律对算法进行性能优化,使得约束函数的时间复杂度从O(n)降至O(1),相信是每一个算法爱好者所关心的。今天写这篇文章的目的便是带大家一起探究一下,八皇后问题之中到底存在什么样的数学规律,能够摆脱传统的递归解法,使得性能提升一个量级。
先放出基本递归解法的代码吧!
public int caculate(int[] locationRecord, int n, int curHang, int totalCount) {
for (int i = 0; i < n; i++) {
locationRecord[curHang] = i;
//检查是否和上面已存在的皇后冲突
boolean hasConflict = false;
for (int j = 0; j < curHang; j++) {
if (locationRecord[j] == locationRecord[curHang] || locationRecord[j] - locationRecord[curHang] == curHang - j || locationRecord[curHang] - locationRecord[j] == curHang - j) {
hasConflict = true;
break;
}
}
if (hasConflict == false) {
int nextHang = curHang + 1;
if (nextHang == n) {
totalCount++;
} else {
totalCount = caculate(locationRecord, n, nextHang, totalCount);
}
}
}
return totalCount;
}
想必大部分读者都掌握了基本的递归解法,这里大致介绍一下吧。首先强调一下规则:棋盘中,同一行,同一列,同一条对角线上不能同时存在两个皇后。按照规则,我们可以选择按行来遍历,每一行放置一个,也可以选择按列来遍历,每一列放置一个。上面解法使用按行遍历,从第一行遍历至第八行,每一行找一个合法位置放置皇后。当第八行(最后一行)成功放置皇后之后,便找到一个解,然后return回溯,继续找下一个解。
上述代码中,在每一行元素遍历中,都会检查是否存在冲突,这个检查也成为约束函数。检查的方式很简单,就是开一个for循环,判断当前行的当前元素是否和当前行之上的任一一行中皇后存在冲突,如果冲突就继续遍历下一个元素,如果不冲突就在当前元素位置放置皇后,并继续遍历下一行。这个约束函数的实现很简单,也很直观易懂。但是却造成了O(n)的复杂度,未免有点让人难以接受。
怎么去优化约束函数呢?我们首先来看一下存在冲突时的情况。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
0 | X1 | |||||||
1 | X2 | |||||||
2 | X7 | X3 | ||||||
3 | X8 | X4 | ||||||
4 | X9 | X5 | X10 | |||||
5 | X6 | X11 | ||||||
6 | X12 | |||||||
7 |
现在我们来做出一波分析。X1,X2,X3之间互相冲突,X1坐标为(0,1),X2坐标为(1,2),X3坐标为(2,3),观察可发现一个规律,它们在同一条主对角线上,并且纵坐标减去横坐标值恒为1。同样的,在同一条主对角线上的X7,X8,X9也存在相互冲突,X7坐标(2,1),X8坐标(3,2),X9坐标(4,3),它们的纵坐标减去横坐标值恒为-1。到这里,我们已经发现并确定这个规律了,同一对角线上的元素,它们的横纵坐标差值是相等的。
不不,这话说的太早,哈哈!是不是忽略副对角线的情况了,好的,我们再分析下副对角线的情况。X4,X5,X6在同一副对角线并且相互冲突,X4坐标(3,7,),X5坐标(4,6),X6坐标(5,5),仔细观察可得知,它们的横坐标值加纵坐标值之和恒为10,X10,X11,X12亦是这个规律。
好的,现在规律已经找出来了,后面就是怎么去编程实现啦。
同一对角线的元素既然橫纵坐标差值相等,那么我们就可以通过一个布尔数组来标标识某条对角线是否存在皇后,设这个数组为v[16],比如某个元素位置放置了皇后,则在数组v中以这个元素橫纵坐标差值为下标的地方,设置值为true,之后遍历到该对角线上其他元素时,只需检查一下数组中以橫纵坐标差值为下标处的值为不为true就行,为true则视为存在冲突,当前元素位置不可取。这个检查只需一步操作就行,也就是O(1)的复杂度。
是不是很激动,运用一个数学小规律,就把O(n)的复杂段降为了O(1)。
好了,八皇后问题规律总结到这里就结束啦,大伙儿可以操刀写一波代码了!顺便看下耗时是不是缩短了,嘿嘿!