八皇后及回溯算法

   记着刚接触到八皇后的问题时,自己总是想不通如何使判断步骤退回来,自己套了好多个循环,最后还是这种情况
 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中排法
八皇后
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值