为啥要学习算法?

131 阅读2分钟

昨天晚上和今天早上看了几节数据结构和算法的课,通过一个简单的例子对比说明了为什么要学习算法.很有说服力,并不是算法用不上,而是层次还不够.一个好的算法和一个差的算法的区别简直是天差地别.

例子的问题是,给定一个n,求n对应的斐波那契数.

斐波那契数列是如下一个数列

0,1,1,2,3,5,8,13

从第三位起的每一位都等于前两位的和

求第N位的斐波拉切数?

一个解法是:

function fib(n){
    if (n<0) {
        throw 'n必须为正整数'
    }
    if(n<=1){
        return n
    }
    return fib(n-2)+fib(n-1)
}

尝试可以发现,当n为40的时候,计算就会很慢,增加到45就卡主了.

真实的开发中如果这么卡,肯定是要出事情的,因而要优化,有没有更好的更快的解法呢?

function fib2(n){
    if (n<=1) {
        return n
    }
    let result = 0
    let first = 0
    let second = 1
    for (let index = 1; index < n; index++) {
        // result为第一个和第二的和
        result = first + second;
        // 更新firstsecond,firstsecond,secondresult
        first = second
        second = result
    }
    return result
}

这种解法,即使n为60,也是瞬间就计算出答案,和上面的解法的效率相比简直天壤之别

这个对比,充分印证了,好的算法和差的算法之间的区别是多么的大.

那么怎么衡量一个算法优劣呢?

最简单的可以通过打印二者的耗时

为了便捷,假设每条指令的执行时间差不多,我们统计执行的指令条数来确定时间复杂度.

老师又举了几个例子,不同的代码,需要的时间复杂度是不一样

通常使用大O表示法

O(1)

O(n)

O(n^2)

O(logn)

O(nlogn)

O(n^2)

O(n^3)

粗略的统计一下代码的时间复杂度以确定算法是否更高效. 比如上面的两段代码 第一种解法的时间复杂度取决于函数调用了多少次

可以看到,当N为5的时候总共有15次调用 当N为4的时候有9次调用,等价于2^3+1 当N为3的时候有5次调用,等价于2^2+1 当N为2的时候有3次调用,等价于2^1+1 当N为1的时候有1次调用,等价于2^0 从N为2开始,调用相当于2^N-1次,所以时间复杂度应该是O(2^n)

第二种解法的耗时主要取决于循环的次数,所以时间复杂度应该是O(n)

为什么我们没有一句一句的数呢?因为规则是,可以忽略常数,乘法,1,2,3这种都是常数,都是O(1),n,2n都视为O(n)