昨天晚上和今天早上看了几节数据结构和算法的课,通过一个简单的例子对比说明了为什么要学习算法.很有说服力,并不是算法用不上,而是层次还不够.一个好的算法和一个差的算法的区别简直是天差地别.
例子的问题是,给定一个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;
// 更新first和second,first为second,second为result
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)