八皇后及回溯算法

   记着刚接触到八皇后的问题时,自己总是想不通如何使判断步骤退回来,自己套了好多个循环,最后还是这种情况  1  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  1  0  0  1  0  0  0  0  0  0  0  0  0  1  0  0  0  0  0  0  0  0  0  
1  0  0  0  0  0  0  0  0  0  0 后来想一想,诶呀,我总顺着往下,在没位置可填的情况下,无法进行新的路线, 也就是我写了好多好多,循环,却只是这一种情况,那是因为什么呢? 就是因为没有往回走。 上面那张情况就是前7行都很符合规定的填入了1,1代表有皇后。 然后到第8行的时候,每个位置都判断失败,也就是说倒数第二行的倒数第三个数就是个死路入口(红色1)!

然后我就想干脆用递归,如果本次(fx(arr,i))有位置可填入的话就调用自己fx(arr,i+1),让下一行进行判断填入操作。判断如果本行没有位置可填入1的话,就代表上一行某个位置是死路,我就清除上一行,调用自己,参数设置为i-1.  fx(arr,i-1)然后从上一行重新选位置。 但是该选哪个位置呢?从开头0下标选的话还是会选到那个死路入口啊。

然后我又想干脆函数设置的参数再多一个,传进去上一行填入1的那个位置的下标的参数 总可以了吧。 假如第8行失败了,没有找到符合的位置然后调用fx(arr,i-1,k+1) 让每次遍历某行时从k+1下标开始总可以了吧。 然而还是失败了,先不说里面有很多逻辑错误,例如连着2次失败的话,无法找到2行前的下标值,什么意思呢? 就是如果8行失败了,调用个fx(arr,i-1,k+1);  到第7行,从k+1  (5 )开始,如果也全失败了无位置可填怎么办,继续返回fx(arr,i-1,k+1)吗?显然不行的,因为k+1是第7行传入的(k+1)+1两次调用后得来的,值为7   但此时第6行明明是下标为3的地方填的是1,这明显不正确?

然后我又加入了好多判断条件呀。如果是因为失败返回的带个flag=false的参数,又要获取它上一行哪个地方是1的下标,然后从下标处开始遍历呀。。。。

其实到最后先不说是否逻辑上有问题,我运行后直接爆栈了!!复杂的判断导致多次递归,无法释放,最后结果没出来,已经瘫痪了。

现在想想还是觉得太傻了,因为当时根本没有理解回溯的思想,对于递归的掌握也很模糊,所以导致无法及时给出递归的出口,1m的空间根本不够我胡折腾!

后来也是看了些有关回溯的资料才突然恍然大悟!! 话转回来,不管怎么样,总之现在搞清楚了,也比一直蒙圈的要强。 以下讲讲回溯法和八皇后的解法吧 其实比起简单的递归来说,就是多了个深度(出口的另一种说法),还有走到死路后回溯的到死路前一个路口的做法。一般是循环遍历到下一个数。 然后做善后工作,意思就是遇到死路后的善后做法。比如8皇后,如果一条路死了,自然要把死路前那行的皇后给清0了,否则循环遍历到下一个数怎么也无法判断可放位置了。

我们先引入一个判断函数;它的参数为arr整数数组,行列下标 bool Queen(int *arr, int h,int l); 它返回的是bool值,如果传入的那个arr[h][l]在当前arr数组中合法符合放皇后的规则,就返回true值, 如果不合法就返回false

以下为简单的示意代码。 例如定义递归的函数名就是寻找皇后! 递归的函数: i为层数的下标,即是8*8数组的行数的下标 void 寻找皇后(int i) {   if(n>7)  //因为8*8数组的行下标是0-7.如果大于7了。就代表这个i是从上个递归的7传下来的。就意味着                      //前8行全部合法,且都有放入皇后,那么我们就该打印结果了。可以把这个n>7称之为探索深度,                      //或者是出口    {     show();   //进入if就代表布局成立,打印出来

   }

   else     {                      for(int  j=0;j<8;j++)             {                if(Queen(arr,i,j))                  {                    寻找皇后(arr,i+1);                  }                 arr[i][j]=0;                      //这就是所谓的遇到死路后的善后工作,如果上面有进入 寻找皇后 函数                                                      // 不管它下面展开的递归是失败了(遇到死路)或成功了(找到出口),                                                    //那些递归下去的函数始终会执行完它们自己的代码,然后结束,收束上来                                                  //或者称回溯上来。这条路已经不能走了(是死路或者有出口,有出口意味着                                                 //第8排能放值,8皇后布局成立,已经打印到屏幕了,但我们要的是所有8皇后                                               //成立的情况,所以仍然要回溯上来进行下一种布局的判断)                                             //回溯上来后,要舍弃刚走过的路,所以把arr[i][j]置为0,然后函数继续运行,会进                                            //入下一次循环,使j++;这就意味着刚舍弃的路的右边一条路开始寻求路线了,符                                          //合我们的预期!!              }     }

}

有人会问怎么没有出现过 寻找皇后(i-1)   的字眼呢,不-1怎么跳到上一行进行回溯啊? 其实当初我也对此困惑了,但后来把递归简单的画一画就一目了然了! 递归出的新函数,如果因为任何原因死路了,它就会运行结束,就是栈的原理,它一结束,就该轮到递归出它的父函数运行了(它的父函数之前卡在for循环里某一环就 递归了,在递归出的新函数完成前,父函数本身是暂挂在栈中的)。它的父函数的i早已经确定了,虽然父函数的i和父函数递归出来的函数(后面且称之为子函数吧) i的值没有什么赋值关系。但其实父函数的i相比子函数的i就已经少过1了(因为我们每次递归都是寻找皇后i+1的传参)。不需要我们再自作聪明传个什么i-1之类的,完全弄巧成拙之举。

最后贴上自己的比较拙劣的代码,虽然比起各种修剪过的代码来说效率不高,多余操作,多余判断没有省略,代码也不精简,但我认为还是很容易理解的。
《八皇后及回溯算法》

《八皇后及回溯算法》

结果:一共92中排法
《八皇后及回溯算法》

    原文作者:八皇后问题
    原文地址: https://blog.csdn.net/xx123427/article/details/78170868
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞