n皇后的一个最普遍的解法便是使用递归,然而,解决n皇后问题时,在main函数里往往只有一个函数调用语句:
eight_queen(0,n);
没错,只用了这一个函数便计算出了n皇后问题的结果,是不是感觉很神奇?
这就是递归的妙处。
其实仔细阅读代码,我们就会发现,在运行过程中,并不是只调用了这一个函数,而是通过这个函数不断调用自身,形成一个循环,然后找到问题的解。
我们看看下面的代码:
void eight_queen(int index,int n)//index是行,n表示要放n个皇后
{
int loop;
for (loop = 0; loop < n; loop++)
{
if (check_pos_valid(index, loop))
{
gEightQueen[index] = loop;
if (n-1 == index)
{
gCount++;print(n);
return;
}
eight_queen(index + 1,n);
}
}
}
n=4时具体调用过程如下:
gEightqueen[0]=0,gEightqueen[1]=2,gEightQueen[2]=无解,返回上一步继续检查后面的loop值;
gEightQueen[1]=3,gEightQueen[2]=2,gEightQueen[3]=无解,返回上一步继续检查后面的loop值;
gEightQueen[2]=无解,gEightQueen[1]=0返回上一步继续检查后面的loop值;
gEightQueen[1]=无解,返回上一步继续检查后面的loop值,没有return;
gEightQueen[0]=1,gEightQueen[1]=3,gEightQueen[2]=0,gEightQueen[3]=2,index=n-1,return;//这里只是return了index为n-1使的函数其它函数还在调用中;
gEightQueen[2]=无解,返回上一步继续检查后面的loop值;
gEightQueen[1]=无解,返回上一步继续检查后面的loop值;
gEightQueen[0]=2,gEightQueen[1]=0,gEightQueen[2]=3,gEightQueen[3]=1,index=n-1,return;//这里只是return了index为n-1使的函数其它函数还在调用中;
gEightQueen[0]=3,gEightQueen[1]=0,gEightQueen[2]=无解,返回上一步继续检查后面的loop值;
gEightQueen[1]=1,gEightQueen[2]=无解,返回上一步继续检查后面的loop值;
gEightQueen[1]=无解,返回上一步继续检查后面的loop值;
gEightQueen[0]=无解,函数整体调用结束;
总结一下递归的思路:
假设函数eight_queen(index,n)为a,函数eight_queen(index,n)为b。
当前调用的函数a循环未执行完毕时,如果出现了可以调用函数中另一个函数b的情况,会先调用b,但当函数b执行完毕时,应继续执行函数a未执行完的部分,最坏的情况下,所有函数的所有情况都会被执行一遍,算法复杂度为O(n^n),但当函数a所有情况都无法满足执行函数b的条件时函数b就不会执行,因此实际调用过程中复杂度往往达不到O(n*n)
涉及到回溯法的程序往往使用递归,遍历所有可能得到解的过程。