1.算法是什么
-
算法是解决某种问题的办法,对于一个问题可以有多种不同的算法,编程语言只是实现算法的一种方式。
例如一道简单的应用题:小明有10个糖,小红有2个糖,小刚有3个糖,问小明和小红的糖的总数比小刚多多少?
如果用数学算式来表示就应该是
小明小红总共:10+2=12(个) 12-3=9(个) 答:他们总共比小刚多9个糖
如果用编程语言来表达就是
public int count(int ming, int hong, int gang){ int sum = ming + hong; int result = sum - gang; return result; }
-
如果多种方式都可以解决这一个问题,那么我们就需要对这些方式进行判断,到底哪一种方式是最佳方式,这里就涉及到了复杂度的概念。如以上算法用编程语言也可以表示为:
public int count(int ming, int hong, int gang){ return ming + hong - gang; }
2.复杂度
复杂度指的是算法执行时所占用的资源,包括两个方面的复杂度:1.时间复杂度。2.空间复杂度。
-
时间复杂度
时间复杂度指的是执行该算法所耗费的时间。
我们可以将一条命令的执行的时间复杂度看为1,那么上面第一个程序的时间复杂度可以大致看为3,第二个程序的时间复杂度可以看为1,很明显后者的时间复杂度要低。
-
空间复杂度
空间复杂度指的是执行该算法所占用的内存空间。
每一个变量都会占用一个内存空间,上面的第一个程序占用的了2个内存空间,第二个程序并没有占用内存空间,因此后者的空间复杂度也更低。
3.复杂度的估算
这里我们以时间复杂度为例,以大O表示法表示算法的复杂度
-
如果执行一个算法需要执行的命令次数为常数,那么可以将常数忽略,此时时间复杂度为O(1)。
-
如果执行一个算法需要执行的命令次数为n次,那么时间复杂度为O(n)。即使执行的次数可能为n+1次或者2n,3n次等,依旧将其看作O(n)。
-
如果执行logn次,那么不管底数为多少,即使两个对数相加,都将其忽略,时间复杂度都为O(logn)。当然,如果是n倍的logn则前面的n不能忽略,时间复杂度应该是O(nlogn)。
例如下图:
大O表示法的时间复杂度排序:
O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
4.一个好的算法应该具备哪些特征
- 正确性、可读性、健壮性。
算法必须要保证正确,不正确的算法是没有必要衡量其好坏的;算法也要保证良好的可读性,能够让阅读者明白内在实现与逻辑;健壮性为对不合理输入的反应能力和处理能力,比如非法输入,要有相应的处理,而不应该程序奔溃等。这些都是一个良好的算法必备的条件。 - 时间复杂度
时间复杂度也是一个衡量算法优劣的重要条件,不同的算法的执行时间可能会存在很大的差别。 - 空间复杂度
空间复杂度表示一个算法执行过程中,需要的空间(内存)数量,也是衡量一个算法的重要指标,尤其是在嵌入式等程序中的算法,内存是非常宝贵的,有时候宁愿提高时间复杂度,也要保证不占用太多的空间。
随着技术的发展,空间存储的问题得到了很好的解决,所以现在最主要关注的是时间复杂度
5.斐波那契数的时间复杂度
这里通过一个计算斐波那契数来对比一下不同的算法时间复杂度的差距究竟有多大。
斐波那契数是从零开始,后一个数等于前两项之和,0,1,1,2,3,5,8,13
要求给一个n的值,算出第n个位置上的数字是多少,根据这个问题,有以下两个算法来实现,fab1和fab2。
/**
* 第一种方式,使用递归的方式求解
* @param n
* @return
*/
public static int fib1(int n){
if (n <= 1){
return 0;
} else if (n <= 2){
return 1;
}
return fib1(n - 1) + fib1(n - 2);
}
/**
* 第二种方式,使用for循环
* 求出第n个数为多少
* @param n
* @return
*/
public static int fib2(int n){
//定义第一个和第二个数,第三个数等于两者之和
int first = 0;
int second = 1;
//定义结果
int result = 0;
//第n个数为前两者之和,这里使用for循环来对前面的数进行相加
//如果需要计算第n个数,则需要进行n-1次相加
for(int i = 1; i <= n-1; i++){
result = first + second;
first = second;
second = result;
}
return result;
}
fab1
fab1用到了递归,而每次递归都会调用该方法2次,依次调用下去可以得出该时间复杂度为O(2^n),这是相当大的时间复杂度了。
fab2
再看fab2,需要的时间复杂度为1+1+1+1+2(n-1)+3(n-1),最后可以表示为O(n),算是比较小的时间复杂度了。