1.算法初体验
1.1 需求1:计算1到100的和。
第一种解法:
public static void main(String[] args) {
int sum = 0;
int n=100;
for (int i = 1; i <= n; i++) {
sum += i;
}
System.out.println("sum=" + sum);
}
第二种解法:
public static void main(String[] args) {
int sum = 0;
int n=100;
sum = (n+1)*n/2;
System.out.println("sum="+sum);
}
第一种解法要完成需求,要完成以下几个动作:
1.定义两个整型变量;
2.执行100次加法运算;
3.打印结果到控制台;
第二种解法要完成需求,要完成以下几个动作:
1.定义两个整型变量;
2.执行1次加法运算,1次乘法运算,一次除法运算,总共3次运算;
3.打印结果到控制台;
很明显,第二种算法完成需求,花费的时间更少一些。
1.2 需求2:计算10的阶乘
第一种解法:
public class Test {
public static void main(String[] args) {
//测试,计算10的阶乘
long result = fun1(10);
System.out.println(result);
}
//计算n的阶乘
public static long fun1(long n){
if (n==1){
return 1;
}
return n*fun1(n-1);
}
}
第二种解法:
public class Test {
public static void main(String[] args) {
//测试,计算10的阶乘
long result = fun2(10);
System.out.println(result);
}
//计算n的阶乘
public static long fun2(long n){
int result=1;
for (long i = 1; i <= n; i++) {
result*=i;
}
return result;
}
}
第一种解法,使用递归完成需求,fun1方法会执行10次,并且第一次执行未完毕,
调用第二次执行,第二次执行未完毕,调用第三次执行...最终,最多的时候,需要在
栈内存同时开辟10块内存分别执行10个fun1方法。
第二种解法,使用for循环完成需求,fun2方法只会执行一次,最终,只需要在栈
内存开辟一块内存执行fun2方法即可。
很明显,第二种算法完成需求,占用的内存空间更小
2 算法分析
前面我们已经介绍了,研究算法的最终目的就是如何花更少的时间,如何占用更少的内存
去完成相同的需求,并且也通过案例演示了不同算法之间时间耗费和空间耗费上的差异,
但我们并不能将时间占用和空间占用量化,因此,接下来我们要学习有关算法时间耗费
和算法空间耗费的描述和分析。有关算法时间耗费分析,我们称之为算法的时间复杂度分
析,有关算法的空间耗费分析,我们称之为算法的空间复杂度分析。
2.1 算法的时间复杂度分析
我们要计算算法时间耗费情况,首先我们得度量算法的执行时间,那么如何度量呢?
- 事后分析估算方法:
比较容易想到的方法就是我们把算法执行若干次,然后拿个计时器在旁边计时,这种事后
统计的方法看上去的确不错,并且也并非要我们真的拿个计算器在旁边计算,因为计算机
都提供了计时的功能。这种统计方法主要是通过设计好的测试程序和测试数据,利用计算
机计时器对不同的算法编制的程序的运行时间进行比较,从而确定算法效率的高低,但是
这种方法有很大的缺陷:必须依据算法实现编制好的测试程序,通常要花费大量时间和精
力,测试完了如果发现测试的是非常糟糕的算法,那么之前所做的事情就全部白费了,并
且不同的测试环境(硬件环境)的差别导致测试的结果差异也很大
- 事前分析估算方法:
在计算机程序编写前,依据统计方法对算法进行估算,经过总结,我们发现一个高级语言
编写的程序程序在计算机上运行所消耗的时间取决于下列因素:
1.算法采用的策略和方案;
2.编译产生的代码质量;
3.问题的输入规模(所谓的问题输入规模就是输入量的多少);
4.机器执行指令的速度;
由此可见,抛开这些与计算机硬件、软件有关的因素,一个程序的运行时间
依赖于算法的好坏和问题的输入规模。如果算法固定,那么该算法的执行时间就只和问题的输入规模有关系了。
2.2 算法的空间复杂度分析
- 类似于时间复杂度的讨论,一个算法的空间复杂度(Space Complexity)定义为该算法所耗费的存储空间,它也是问题规模 n 的函数。
- 空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。有的算法需要占用的临时工作单元数与解决问题的规模 n 有关,它随着 n 的增大而增大,当 n 较大时,将占用较多的存储单元,例如快速排序和归并排序算法, 基数排序就属于这种情况
- 在做算法分析时,主要讨论的是时间复杂度。 从用户使用体验上看,更看重的程序执行的速度。一些缓存产品(redis, memcache)和算法(基数排序)本质就是用空间换时间。