递归(recursion):程序调用自身的编程技巧
递归满足两个条件:
(1)有反复执行的过程(调用自身)
(2)有跳出反复执行过程的条件(递归出口)
递归例子(常用的地方):
(1)阶乘
n != n * (n – 1) * (n – 2) * …* 1 (n > 0)
程序:
#include <stdio.h>
int recursive(int i)
{
int sum;
if(0 == i)
return 1;
else
sum = i * recursive(i - 1); // i * (i - 1) * .... * 1
return sum;
}
int main()
{
int temp;
temp = recursive(5);
printf("temp = %d\n",temp);
}
(2)河内塔问题
分析:汉诺塔的算法3个步骤:
第一,把a上n-1个盘通过c移动到b。
第二,把a上最下面的盘移到c。
第三,因为n-1盘全在b上了,所以把b当做a重复以上步骤就好了。
所以把b当做a重复以上步骤就好了。
算法看起来简单,但是思考的过程很复杂,难以理解。递归中会保存数据好处在这里有得到体现。
#include <stdio.h>
int count = 0;
//定义函数n是要移动的个数,a移动到c通过b
void hanoi(int n,int a,int b,int c)
{
if(n >= 1)
{
hanoi(n-1,a,c,b); //n - 1 move to b by c
printf("%c--%c\n",a,c);//print process
count++;
hanoi(n-1,b,a,c); //move to c by a
}
}
int main()
{
int n;
printf("Input the number of diskes:\n");
scanf("%d",&n);
hanoi(n,'A','B','C');
printf("count = %d\n",count);
}
对递归的一些理解:
解决问题、不能太去关心细节(因为递归的过程恰恰是我们实现的方法)就像中国问题,如果我们在第一步就过多的纠结于如何把n-1移动到B上,那么你的思路就很难继续深入。只要看做是用函数实现就好,如果你看出不管怎么移动其实本质都一样的时候,那么久能较快的得到结果了。
思考问题讲究思路的连贯性、力求尽快进入状态,享受完全投入到一件事中的每秒感觉。
(3)全排列
从n个不同元素中任取m(m <= n)个元素,按照一定的顺序排列起来,叫做从不同元素中取出m个元素的一个排列,当m = n所有的排列情况就叫全排列。
如1、2、3三个元素全排列为:
1,2,3
1,3,2
2,1,3
2,3,1
3,1,2
3,2,1
全排列有两种算法:
一种是递归,一种是非递归。
递归算法的思路比较接近于我们现实生活中的思路。
1、试想,如果我们只有两个数字:12,要对它进行排列,第一种就是12本身,第二种,将12交换,变为21.
2、但是如果我们要对123进行全排列。我们可以采用将1固定,“23”进行全排列,将“2”固定,对“13”进行全排列。将“3”固定,对“12”进行全排列。
其实这就是首部为“1”,然后是“2”,然后是“3”,不就是第二位后边的数依次和第一位进行交换么?
3.但是这样也不全面,我们每次交换要将排列恢复成原来的“123”,因为这个算法求排列的时候,前后并没有依赖性,其参考物只有“123”这个原始的第一个排列。否则,我们不恢复的话,就会出现,虽然数量与正确解法相同,但是会有重复排列的现象。
(4)斐波那契数列
斐波那契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21….
#include <stdio.h>
long fob(int n)
{
if(n == 0)
{
return 0;
}
if(n == 1)
{
return 1;
}
if(n > 1)
{
return fob(n-1) + fob(n - 2);
}
}
int main()
{
int i;
int result;
printf("please input number:\n");
scanf("%d",&i);
result = fob(i);
printf("result = %d",result);
}