递归与回溯:
a.回溯算法的基本思想:从问题的某一种状态出发,搜索可以到达的所有状态。当某个状态到达后,可向前回退,并继续搜索其他可达状态。当所有状态都到达后,回溯算法结束!
b.
对于回溯算法,在前面KMP匹配中就利用了这个思想,只不过当时KMP中定义了一个node数组(起到了一个地图的作用,记录了每种回溯情况的可能)。而这节中,是利用函数的活动对象保存回溯算法的状态数据,因此可以利用递归完成回溯算法!
2.八皇后问题:
a.问题背景:国际象棋是一个8*8的矩阵,在棋盘中同时放下8个皇后,且互相不攻击的情况叫八皇后问题
b.如图,说明八皇后问题中的回溯算法:

注意:其实就是不断的通过递归函数,去往棋盘中尝试放皇后,成功就继续递归(即继续放皇后),失败就跳出递归函数,回溯到上层递归函数中,上层递归函数中保存着上一个皇后的位置!!!这就是八皇后中,回溯的概念!
c.
八皇后的算法思路:
第一、为了更加方便我们表示棋盘,我们使用一个10*10的二维数组来表示8*8的棋盘加棋盘边框。
- char board[10][10];
- void init()
- {
- int i = 0;
- int j = 0;
- for(i=0; i<N+2; i++)
- {
- board[0][i] = '#';
- board[N+1][i] = '#';
- board[i][0] = '#';
- board[i][N+1] = '#';
- }
- for(i=1; i<=N; i++)
- {
- for(j=1; j<=N; j++)
- {
- board[i][j] = ' ';
- }
- }
- }
- void display()
- {
- int i = 0;
- int j = 1;
- for(i=0; i<N+2; i++)
- {
- for(j=0; j<N+2; j++)
- {
- printf("%c", board[i][j]);
- }
- printf("\n");
- }
- }
- typedef struct _tag_pos //定义一个数据结构来充当方向
- {
- int i;
- int j;
- }Pos;
- /*检测三个方向 左上 右上 正上 横排是不检测的 因为一排只放一个*/
- static Pos pos[3] = {{-1,-1},{-1,1},{-1,0}};
- int check(int i, int j)
- {
- int p = 0;
- int ret = 1;
- for(p = 0; p < 3; p++) //检测三个方向
- {
- int ni = i;
- int nj = j;
- while(ret && (board[ni][nj] != '#'))//判断没有到达棋盘边界
- {
- ni = ni + pos[p].i;
- nj = nj + pos[p].j;
- ret = ret && (board[ni][nj] != '*');//判断这个方向没有放过皇后
- }
- }
- return ret; //可以放皇后返回1 不可返回0
- }
- void find(int i)
- {
- int j = 0;
- if(i > N) //判断是否已经超过了第八行
- {
- coust++; //计算八皇后情况的个数
- display();
- //getchar();
- }
- else
- {
- for(j = 1; j <= N; j++) //判断一行 是否有匹配的位置
- {
- if(check(i,j))
- {
- board[i][j] = '*'; //放置皇后
- find(i+1);
- board[i][j] = ' '; //清除放错的皇后
- }
- }
- }
- }
看着上面的动态想象下,当有时候,出现过的皇后又消失了,就是因为for循环结束了,还没有check成功,导致递归结束,返回到上层递归,并且擦除上层皇后的位置,并且继续for循环,如果这层for中check依然不成功,继续回溯,这个消失的过程就是回溯过程!与动图不同的是,我们的程序,不会在找完一种情况后就结束!即使成功匹配,程序也会向上层回溯,去寻找其它的情况,直到第一行for循环结束(即 i=1 的时候),程序才会停止!
本节代码:
八皇后问题的完整代码:- #include <stdio.h>
- #define N 8
- static char board[N+2][N+2];
- static int coust = 0; //记录八皇后个数
- typedef struct _tag_pos //定义一个数据结构来充当方向
- {
- int i;
- int j;
- }Pos;
- /*检测三个方向 左上 右上 正上 横排是不检测的 因为一排只放一个*/
- static Pos pos[3] = {{-1,-1},{-1,1},{-1,0}};
- void init()
- {
- int i = 0;
- int j = 0;
- for(i=0; i<N+2; i++)
- {
- board[0][i] = '#';
- board[N+1][i] = '#';
- board[i][0] = '#';
- board[i][N+1] = '#';
- }
- for(i=1; i<=N; i++)
- {
- for(j=1; j<=N; j++)
- {
- board[i][j] = ' ';
- }
- }
- }
- void display()
- {
- int i = 0;
- int j = 1;
- for(i=0; i<N+2; i++)
- {
- for(j=0; j<N+2; j++)
- {
- printf("%c ", board[i][j]);
- }
- printf("\n");
- }
- }
- int check(int i, int j)
- {
- int p = 0;
- int ret = 1;
- for(p = 0; p < 3; p++) //检测三个方向
- {
- int ni = i;
- int nj = j;
- while(ret && (board[ni][nj] != '#'))//判断没有到达棋盘边界
- {
- ni = ni + pos[p].i;
- nj = nj + pos[p].j;
- ret = ret && (board[ni][nj] != '*');//判断这个方向没有放过皇后
- }
- }
- return ret; //可以放皇后返回1 不可返回0
- }
- void find(int i)
- {
- int j = 0;
- if(i > N) //判断是否已经超过了第八行
- {
- coust++; //计算八皇后情况的个数
- display();
- //getchar(); //起到断点的作用
- }
- else
- {
- for(j = 1; j <= N; j++) //判断一行 是否有匹配的位置
- {
- if(check(i,j))
- {
- board[i][j] = '*'; //放置皇后
- find(i+1);
- board[i][j] = ' '; //清除放错的皇后
- }
- }
- }
- }
- int main()
- {
- init();
- //display();
- find(1);
- printf("coust is %d \n",coust);
- return 0;
- }