从n皇后问题理解递归与回溯

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)

        涉及到回溯法的程序往往使用递归,遍历所有可能得到解的过程。

猜你喜欢

转载自www.cnblogs.com/xbnl-bk-zm-2018/p/11422235.html