算法学习 - 01 递归讨论

偶尔看到一篇博客,http://blog.csdn.net/feixiaoxing/article/details/6838362,一步一步写算法,觉得挺不错,坚持不是一件容易的事,当偶尔有空时候看看算法觉得时间还是过的挺快的,算是打算时间吧,同时也锻炼锻炼别让脑子生锈了。。。

很害怕递归,莫名的,就从这里开始吧。。。

首先来两个简单的例子:

1. 计算从1到m的和

很自然,用循环就实现啦

//计算从1加到m的和  --- 循环实现
int calculatorLoop(int m)
{
    int sum = 0;
    if(m < 1)
        return -1;
    for(int index=1; index<=m; ++index)
        sum += index;
    return sum;
}

接下来就是坑爹的递归了。。。

//计算从1加到m的和 --- 递归实现
int calculatorRecursion(int m)
{
    if(m < 1)
        return -1;
    if(m == 1)
        return 1;
    else
        return m + calculatorRecursion(m-1);
}

好像看上去也不是那么难,自己调用自己咯。。。


2. 第二个算法也是很常见的,查找:在数组中查找指定的元素,返回其索引(规定索引从0开始)

当然,还可以用循环

//从数组a中查找指定的元素,返回索引index(从0开始) -- 循环实现
int findLoop(int *a, int len, int value)
{
    if(a == NULL || len == 0)
        return -1;
    for(int index=0; index<len; ++index)
    {
        if(value == *(a+index))
            return index;
    }
    return -1;
}

这次递归就不那么容易了,稍微思考下:

int _findRecursion(int index, int *a, int len, int value)
{
    if(index == len-1)
        return -1;
    if(value == *(a + index))
        return index;
    return _findRecursion(index+1, a, len, value);
}

//从数组a中查找指定的元素,返回索引index(从0开始) -- 递归实现
int findRecursion(int *a, int len, int value)
{
    if(a == NULL || len == 0)
        return -1;
    return _findRecursion(0, a, len, value);
}

哈哈,要点就在那个index,这是递归前进的依据。。。



两个小例子就这样,讲完了,现在稍微总结下:

1. 递归是直接或间接调用自身的一种方法。递归策略之需要少量的程序就能描述出解题过程所需要的多次重复计算,精华在于用有限的语句定义对象的无限集合。

一般,递归需要有边界条件,条件不满足递归前进,满足递归返回。


2. 递归算法一般用于解决三类问题:数据按递归定义;问题解法是递归算法;数据结构形式按递归定义;


3. 递归有致命的缺点:效率低下。递归的过程中每一层的返回点和局部变量都是开辟栈存储,递归次数过多容易造成栈溢出。应尽量避免使用递归而采用循环等。


下面就是一些递归的经典实例:

1. 斐波纳契数列:1,1,2,3,5,8,13,21.。。。。

前两位确定,后面的依次是前两位的和。数学表达式:F(n) = F(n-1) + F(n-2);

好吧,上段代码:打印斐波纳契数列的前100个数

//返回斐波纳契数列中第index个
int fibonacci(int index)
{
    if(index < 1)
        return -1;
    if(index == 1 || index == 2)
        return 1;
    return fibonacci(index-1) + fibonacci(index-2);
}

int main()
{
    for(int i=1; i<=20; ++i)
    {
        cout << fibonacci(i) << endl;
    }
    return 0;
}

2. 兔子大家都喜欢吧,哈哈,有个故事,第一个月有一对兔子,两个月后也就是第三个月初可以生育,每月可生育的兔子会诞生一对新兔子,假设兔子不死。

那么第n个月后剩下多少兔子。分析后发现兔子对数随着月的递增呈现:1,1,2,3,5,8.。。。


3. 下面介绍一个生活中的场景:早上照镜子,不知道谁家的镜子是两个,而且是面对面的,那就好玩了,无穷递归下你会看到无穷多个自己。。。


4. 这个是数学上的,计算阶乘,这个上升的很利害,搞不好就超出了范围

代码如下:在int有效表示范围内可以

//计算m的阶乘
int fac(int m)
{
    if(m < 0)
        return -1;
    if(m == 1)
        return m;
    return m * fac(m-1);
}

5. 这个比较好玩,ACM上看到的,走台阶问题,假设n阶台阶,你可以每次走一个台阶,也可以两个,问有多少种走法?

当只有1个台阶:f (1) = 1;   2个台阶时候 ,f(2) = 2; 

当3个台阶,f(3) = 3

当n个台阶,f(n) = f(n-1) + f(n-2);


6. 哈哈,有难度了,汉诺塔问题:

有一个梵塔,塔内有三个座A、B、C,A座上有诺干个盘子,盘子大小不等,大的在下,小的在上(如图)。

把这些个盘子从A座移到C座,中间可以借用B座但每次只能允许移动一个盘子,并且在移动过程中,3个座上的盘

子始终保持大盘在下,小盘在上。


//汉诺塔问题
void hanoi(int n, char A, char B, char C)
{
    if(n == 1)
        cout << "move disk " << n << " from " << A << " to " << C << endl;
    else
    {
        hanoi(n-1, A, C, B);
        cout << "move disk " << n << " from " << A << " to " << C << endl;
        hanoi(n-1, B, A, C);
    }
}

int main()
{
    hanoi(3, 'A', 'B', 'C');   //假设三个盘子
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值