回溯是一种思维,而递归(迭代)是一种实现回溯思维的编程方法;
回溯 : 是一种试错的思维方法,对于一些不能够通过表达式或者解析式描述的问题,或者有表达式但是实现起来相当复杂的一些算法,就是用于回溯法,特别是一些深度优先搜索(所搜树)等等问题,比如下面要说的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语言一步一步调试,这样是最清楚的