应用实战(一)
递归的数学思想
-
递归是一种数学上分而自治的思想
-
递归将大型复杂问题转化为与原问题相同但规模较小的问题进行处理
-
递归需要有边界条件
-
当边界条件不满足时,递归继续进行
-
当边界条件满足时,递归停止
-
递归的数学表示
斐波拉契数列递归解法
#include <stdio.h>
int fibonacci(int n)
{
if( n > 1 )//递推
{
return fibonacci(n-1) + fibonacci(n-2);
}
else if( n == 1 )
{
return 1;
}
else if( n == 0 )
{
return 0;
}
}
int main()
{
int i = 0;
for(i=1; i<=10; i++)
{
printf("fibonacci(%d) = %d\n", i, fibonacci(i));
}
return 0;
}
strlen递归解法
#include <stdio.h>
int strlen(const char* s)
{
if( s == NULL )
{
return -1;
}
else if( *s == '\0' )
{
return 0;
}
else
{
return strlen(s+1) + 1;
}
}
int main()
{
printf("strlen(\"12345\") = %d\n", strlen("12345"));
printf("strlen(NULL) = %d\n", strlen(NULL));
printf("strlen(\"\") = %d\n", strlen(""));
return 0;
}
汉诺塔递归解法
#include <stdio.h>
void hanoi(int n, char a, char b, char c)
{
if( n > 0 )
{
if( n == 1 )
{
printf("%c -> %c\n", a, c);
}
else
{
hanoi(n-1, a, c, b);//从a起始,借助c,移动到b
printf("%c -> %c\n", a, c);
hanoi(n-1, b, a, c);
}
}
}
int main()
{
hanoi(8, 'a', 'b', 'c');
return 0;
}
全排列递归解法
#include <stdio.h>
void permutation(char s[], int b, int e)//数组,起始位置,终止位置
{
if( (0 <= b) && (b <= e) )
{
if( b == e )
{
printf("%s\n", s);
}
else
{
int i = 0;
for(i=b; i<=e; i++)//遍历每个元素
{
char c = s[b];
s[b] = s[i];//把第i个元素交换到第b个
s[i] = c;
permutation(s, b+1, e);//对剩下的全排列,得到部分结果
c = s[b];
s[b] = s[i];
s[i] = c;//交换回来
}
}
}
}
int main()
{
char s[] = "abcd";
permutation(s, 0, 3);
return 0;
}
小结
-
递归是一种将问题分而自治的思想(大问题转化为小问题,小问题和大问题相似,只是规模更小,这样不停的递推,推到非常简单的一个状态)
-
用递归解决问题首先要建立递归的模型
-
递归解法必须要有边界条件,否则将死循环
应用实战(二)
递归与回溯
-
递归在程序设计中也常用于需要回溯算法的场合
-
回溯算法的基本思想
-
从问题的某一种状态出发,搜索可以到达的所有状态
-
当某个状态到达后,可向前回退,并继续搜索其它可达状态
-
当所有状态都到达后,回溯算法结束
-
-
程序设计中可利用函数的活动对象保存回溯算法的状态数据,因此可以利用递归完成回溯算法
八皇后问题
在一个8×8国际象棋盘上,有8个皇后,每个皇后占一格;要求皇后间不会出现相互“攻击”的现象,即不能有两个皇后处在同一行、同一列或同一对角线上。
解决方案
8皇后问题解法
#include <stdio.h>
#define N 8
typedef struct _tag_Pos
{
int ios;
int jos;
} Pos;
static char board[N+2][N+2];//+2是为了做个边界出来
static Pos pos[] = { {-1, -1}, {-1, 0}, {-1, 1} };//方向数组
static int count = 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 ret = 1;
int p = 0;
for(p=0; p<3; p++)//检查三个方向
{
int ni = i;
int nj = j;
while( ret && (board[ni][nj] != '#') )//返回值为真,且没有碰到边界
{
ni = ni + pos[p].ios;
nj = nj + pos[p].jos;
ret = ret && (board[ni][nj] != '*');//有无皇后?
}
}
return ret;
}
void find(int i)
{
int j = 0;
if( i > N )
{
count++;
printf("Solution: %d\n", count);
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();
find(1);
return 0;
}
小结
-
回溯算法是递归应用的重要场合
-
利用函数调用的活动对象可以保存回溯算法中重要的变量信息