关于递归和回溯的说明以及8皇后问题的递归流程分析

回溯是一种思维,而递归(迭代)是一种实现回溯思维的编程方法;


回溯 : 是一种试错的思维方法,对于一些不能够通过表达式或者解析式描述的问题,或者有表达式但是实现起来相当复杂的一些算法,就是用于回溯法,特别是一些深度优先搜索(所搜树)等等问题,比如下面要说的8皇后问题;


递归一般解释就是自己调用自己,他的实现是通过系统的堆栈的完成的,每调用一次自己,当前参数将会被保存到堆栈当中,知道当前级函数返回,就会调用上一级的堆栈数据来继续计算。


八皇后请自行百度


解决八皇后的思路 (n皇后思路类似):

1) 首先将第一个皇后放置在第一行的第一列中;


2) 然后在将第二个皇后放置在第二行中,由于第二行有8个位置可行,所以通过for循环来便利8个位置,直至找到合适的位置并将记录该位置;


3) 然后将第三个皇后放置在第3行中,同样也遍历各个列的位置


以此类推 。。。。


4) 当发现其中第n个皇后遍历完成n行的所有列都不符合,此时我将返回上行,改变上一行皇后的位置,然后继续往下遍历,直到遍历找到一组真确的解


5) 如果找到了一组解之后,即每一行中都存在一个皇后,此时输出当前成立的一组数列,并且将最后一行的换后位置清零,并且认为的将上一行的皇后位置往后在移动一位,再继续遍历,这样可以遍历完成所有的可能结果



根据上述的思路,皇后的位置类似于堆栈的操作,如果发现一个皇后符合条件,则将皇后的位置压栈,如果发现遍历完成所有的列后没有,则出栈并改变上一个栈的位置


代码段如下 :


#include "stdafx.h"
#include "math.h"
#include "stdio.h"
#define cloum 8
#define row 8
#define NumbersQ 8
int index = 0;
short EGITVALUE[NumbersQ+1];
void showe(){
printf("//------------------------------------//\n");
printf("the number of qeun is : %d\n" , index);
short i = 0;
short j = 0;
for(i = 1; i < cloum+1 ; i ++){
for(j = 1; j < cloum+1 ; j ++){
if(EGITVALUE[i] == j) printf("%c " , '#');
else printf("%c " , '0');
}
printf("\n");
}
}
int IsMach(int k){
int i = 1;
while(i<k){
if(EGITVALUE[i] == EGITVALUE[k]) return 0;
if(abs( EGITVALUE[k] - EGITVALUE[i]) == abs(k -i)) return 0;
i++;
}
return 1;
}
//----------------------------------------------
//递归
int quen2(int n){
short j = 0;
for(j = 1; j <= NumbersQ ; j++){
EGITVALUE[n] = EGITVALUE[n] + 1;
if(n == 8 && IsMach(n)){
showe();
index = index + 1;
EGITVALUE[n] = 0;
return 0;
}
else if(IsMach(n)){
quen2(n + 1);
}
}
EGITVALUE[n] = 0;
return 0;
}
//----------------------------------------------
void quen1(){
int k = 1;
while(k >= 1){
while(EGITVALUE[k] < cloum){
EGITVALUE[k] = EGITVALUE[k] + 1;
if(k == 8 && IsMach(k)){
index = index + 1;
showe();
}
else if(IsMach(k)){
k++;
}
}
EGITVALUE[k] = 0;
k--;
}
}
void main()
{
quen2(1);
while(1);
}


pc端的递归过程 :

对于递归的编写思路是这样的,他会与非递归的思想稍微有一些不一样:


具体如程序代码所示 :


1、调用quen2(1);此时表示我输进去的是第一个皇后,即首先找到第一个皇后的位置


2、进入quen2(n)函数之后,一个皇后n,(这个n也代表此皇后所在的行数,两个变量在数值上是一样的)在第n行中有8个位置可以放置,具体放置在哪一个位置,则通过全遍历来确定。所以for循环是从1 : 8次的8次循环。


3、EGINAVLUE[n] ; 他表示第n个皇后在第n行第EGINAVLUE[n] 列;假设此时的n = 1 ; EGINAVLUE[n] = 0;表示了我将第一个皇后放置在了第1行第0列上;由于在回溯过程中会涉及到当前值的自增过程,所以此处都已第一行第一列作为起始位置,所以EGINAVLUE[n] = EGINAVLUE[n]  + 1; 表示当前我是将第一个皇后放置到了第一行第一列的位置。


4、IsMach函数主要试判断当前位置是否合法,他的判断也是比较简单,思路大概是,我将当前皇后所在的行数(次行数是和n值在数值上相等的)输入到IsMach函数中,那么该函数会将该位置与当前皇后行数之间的各行数的皇后做判断,以判断该位置是否合法。


5、此时判断当前n == 8 && IsMach(n);此条件是判断我是否已经放置到了最后一个皇后,并且该位置是合法的,此时输出这一组解,并将当前皇后的位置清零EGINAVLUE[n] = 0;这句话后面再解释


6、如果5的条件不满足,但是满足IsMach(n);则说明,该位置是符合条件的,皇后能够放置在这个位置上,由于在第三步的EGINAVLUE[n] = EGINAVLUE[n]  + 1; 操作,所以EGINAVLUE[n],就相当于记录了当前皇后的位置。于是我在重新调用quen(n+1);表征我去寻找下一个皇后的位置


7、调用quen(n+1)时,系统将n的值存入到堆栈当中,并将n+1的值传递到quen2(n+1)中继续执行


8、此时皇后已经到了n+1个;对于当前的话就是第二个皇后,n = 2;同样n= 2 的这个皇后需要在第2行中遍历所有的位置以确定他的位置


。。。。。。以此类推


9、现在假设n = 7;并且前面6个皇后的位置都已经找到了;在寻找第7个皇后的位置时;for在遍历8列中没有一个位置符合条件,所以结束for循环,并且执行EGINAVLUE[n] = 0;即将第7个皇后的位置清零,一遍下一次来遍历


10、return ; 返回后函数会继续执行上一次调用的quen(n)的函数,只不过此时的n值是6,即上一次的压栈值,


11、继续运行for循环,发现此时for循环的条件满足,并且执行EGINAVLUE[n]  = EGINAVLUE[n]  + 1; 相当于第7行的皇后的位置不可用,所以我会退到上一行,并将上一行的换后位置向后移动一个。


12、之后继续往下判断,如果当前(n = 6)的位置符合条件,将会在去查找n = 7下一行的位置查询


13、这种操作会一直递归下去,知道第一个皇后被移动到最后一个位置,整个函数将会结束


当然 这个最好的观测他的运行过程是通过c语言一步一步调试,这样是最清楚的




猜你喜欢

转载自blog.csdn.net/taiyangshenniao/article/details/53611673