豪放婉约派程序员

导航

 

今天在网上看到两个算法题,分别是跳青蛙问题与变态跳青蛙问题,具体题目如下。

问题

跳青蛙问题:

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

这道题看似简单,但是我看了半天也没想出个突破口,在本子上画来画去也没想到解决的方法。

直到我将台阶的个数于跳法的个数罗列出来才发现端倪:

 

台阶数            跳法数
1                    1
2                    2
3                    3
4                    5
5                    8
...                   ...

这尼马不就是斐波那彻数列么?

这下问题转变成了,如何求斐波那彻数列?

方法分两种:递归和非递归。

其实递归的方法是更加容易理解的,写起来也是最简单的。我们直到斐波那彻数列的公式:

f(n) = f(n-1) + f(n-2), n >= 2

套用公式就能写出递归的方法:

int func(int number) {
    if (number <= 0) {
        return (-1);
    }
    else if (1 == number){
        return 1;
    }
    else if (2 == number) {
        return 2;
    }
    else {
        return func(number - 1) + func(number - 2);
    }
}

关于递归方法时间复杂度,这里就不做研究了,有兴趣的朋友可以看看这篇博客,讲的非常好,因为我看不懂:

http://www.cnblogs.com/python27/archive/2011/12/09/2282486.html

其实递归方法的效率是非常慢的,如果在面试的时候,明明可以用非递归的方法写出答案,你偏偏要用递归,那你就是做死了(真事儿,我朋友面别人就用这标准)。

下面介绍非递归的方法,先来看看代码:

int func(int number) {
    if(number<=3)
        return number; 
    int f2=1; 
    int f1=0; 
    int f=0; 
    for(int i=0;i<number;++i) { 
        f=f1+f2;
        f1=f2;
        f2=f;
    } 
    return f;    
}

很明显,时间复杂度为O(n),比递归的方法快了很多倍。

这种方法巧妙的利用了求解公式,类似于用了两个指针,分别指向了被求数的前两位数,得到被求数后,再将其值赋予其中一个指针,另一个指针则往前挪。

看过了正常的跳青蛙问题,我们再来看变态的跳青蛙。

具体题目如下:

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

套用吴宗宪大哥的话说就是:哎哟,这个就厉害了。

这次青蛙可以任意的选择跳上的台阶数,所以不再是一个标准的斐波那契数列问题,不过没关系,我们先找寻问题的规律,再从规律寻找突破口。

根据题目,有:

f(0) = 1

f(1) = 1

f(2) = f(2-1) + f(2-2)

f(3) = f(3-1) + f(3-2) + f(3-3)

...

f(n) = f(n-1) + f(n-2) + ... + f(n-(n-1)) + f(n-n)

简单的解释一下:例如f(3-1)表示3阶跳了1阶后,剩下的跳阶方法数,f(3-2)表示3阶跳了两阶后剩下的跳阶方法数,以此类推,直到一次跳n阶后,剩下的跳阶方法数。

现在问题明了了很多,但是还不够,我们可以继续对其进行分解:

因为 : f(n) = f(n-1) + f(n-2) + ... + f(n-(n-1)) + f(n-n) = f(0) + f(1) + f(2) + ... + f(n-2) + f(n-1)

所以 : f(n-1) = f(0) + f(1) + ... + f((n-1)-1) = f(0) + f(1) + f(2) + ... + f(n-2)

则: f(n) = f(n-1) + f(n-1) = 2*f(n-1)

现在可以根据这个式子来写出代码了。

同样,我们先写出容易写的递归方法:

    int func(int number) {
        if (number <= 0) {
            return -1;
        } else if (number == 1) {
            return 1;
        } else {
            return 2 * func(number - 1);
        }
    }

然后是非递归的方法:

int func(int number) {

    if (number <= 0) {
        return (-1);
    }

    if (1 == number) {
        return 1;
    }

    int f1 = 1;
    int f = 0;
    for (int i = 0; i < number - 1; i++) {
        f = 2 * f1;
        f1 = f;
    }

    return f;
}

总结

通过对这两个问题的分析和解决,我深刻的明白了一个道理:解决问题之前,我们必须充分的了解问题,把握问题的规律。科学家们一直致力于寻找物质的规律,世界的规律以及宇宙的规律。而算法的美妙之处就在于,你通过不断的训练,能从纷杂的问题表面分析出实质性的规律,就如同在茫茫互联网海洋中,寻找那一段段神秘的代码。

今天的博客就写道这里了,老司机催我上车了。

 

posted on 2016-03-10 20:21  豪放婉约派程序员  阅读(1302)  评论(3编辑  收藏  举报