算法是计算机科学领域最重要的基石之一,但却受到了国内一些程序员的冷落。许多学生看到一些公司在招聘时要求的编程语言五花八门就产生了一种误解,认为学计算机就是学各种编程语言,或者认为,学习最新的语言、技术、标准就是最好的铺路方法。其实大家都被这些公司误导了。编程语言虽然该学,但是学习计算机算法和理论更重要,因为计算机算法和理论更重要,因为计算机语言和开发平台日新月异,但万变不离其宗的是那些算法和理论,例如数据结构、算法、编译原理、计算机体系结构、关系型数据库原理等等。在"开复学生网"上,有位同学生动地把这些基础课程比拟为"内功",把新的语言、技术、标准比拟为"外功"。整天赶时髦的人最后只懂得招式,没有功力,是不可能成为高手的。
可以用一个算法的时间复杂度与空间复杂度来评价算法的优劣。
1. 时间复杂度
当一个算法转换成程序并在计算机上执行时,其运行所需要的时间取决于下列因素
1. 硬件的速度。 如:使用微机还是服务器
2. 书写程序的语言。 实现语言的级别越高,其执行效率就越低
3. 编译程序所生成的目标代码的质量。 对于代码优化比较好的编译程序,其执行时间越少
4. 问题的规模。 这个一个抽象的概念, 如:求100以内的素数与求10000以内的素数的执行时间肯定是不同的
显然,在各种因素都不确定的情况下,很难比较出算法的执行时间。 也就是说,使用执行算法的绝对时间来衡量算法的效率是不合适的。
一个算法的时间复杂度是指:算法运行从开始到结束所需要的时间。
这个时间就是该算法中每条语句的执行时间之和,而每条语句的执行时间是该语句执行次数(即频度)与执行该语句所需时间的乘积。
但是,当算法转换为程序之后,一条语句执行一次所需的时间与计算机的性能及编译程序生成目标代码的质量有关,是很难确定的。
为此, 我们假设执行每条语句所需的时间均为单位时间,在这一假设下,一个算法所花费的时间就等于算法中所有语句的频度之和。
http://blog.csdn.net/booirror/article/details/7707551/
O(1): 算法的执行时间与问题规模n无关
O(1) 常数阶 < O(logn) 对数阶 < O(n) 线性阶 < O(nlogn) < O(n^2) 平方阶 < O(n^3) < O(2^n) < O(n!) < O(n^n)
2. 空间复杂度
空间复杂度即是:算法所需存储空间的度量, 记作S(n) = O(f(n)), n是问题的规模。
这里所说的算法所需的存储空间,通常不含输入数据和程序本身所占的存储空间, 而是指算法对输入数据进行运算所需的辅助工作单元,这类空间也被称为额外空间。
算法的输入数据所占的空间是由具体问题决定的,一般不会因算法不同而改变;算法本身占用的空间不仅和算法有关,而且和编译程序产生的目标代码的质量有关,所以也难以讨论;算法所占的额外空间却是与算法的质量密切相关。
案列分析:
案例一:两个n阶方针的乘积C=A*B的算法
1 $n = 100; 2 3 /** 4 * $a => $arr[$n][$n] 5 * $b => $arr[$n][$n] 6 */ 7 function MatrixMultiply($a, $b) 8 { 9 for ($i=0; $i < $n; $i++) { //n+1 10 for ($j=0; $j < $n; $j++) { //n(n+1) 11 $c[$i][$j] = 0; //n2 12 13 for ($k=0; $k < $n; $k++) { //n2(n+1) 14 $c[$i][$j] = $c[$i][$j] + $a[$i][$k] * $b[$k][$j]; //n3 15 } 16 } 17 } 18 }
右边为语句的频度, 上述算法的执行时间(即语句的频度之和)是:
T(n) = 2n3 + 3n2 + 2n + 1
当n->∞时, T(n) / n3 = (2n3 + 3n2 + 2n + 1) / n3 -> 2
按O的定义我们可知: T(n) = O(n3), 所以该算法时间复杂度是O(n3)
案例二: 递归算法
1 function fact($n) 2 { 3 if ($n <= 1) { 4 return 1; 5 } 6 7 return $n * fact($n-1); 8 }
我们是fact(n)的时间复杂度为T(n), 该算法中n=1时,运行时间为O(1), 另一条语句的运行时间为T(n-1) + O(1), 因此:
如果 n <= 1, T(n) = O(1)
如果n>1, T(n) = T(n-1) + O(1)
则: T(n) = T(n-1) + O(1) = T(n-2) + 2*O(1) = T(n-3) + 3*O(1) = .... = T(1) + (n-1)*o(1) = n*O(1) = O(n)
即fact(n)的时间复杂度为O(n)
案例三: 综合题, 若矩阵Am*n中存在某个元素aij满足:aij是第i行中最小值且是第j列中的最大值,则称改元素为矩阵A的鞍点,试编写算法计算矩阵的鞍点个数。
方法一:用枚举法,对矩阵中的每一个元素aij进行判别,若aij是第i行的最小数,则继续判别,看他是否也是第j列的最大数,如果成立则是鞍点。当aij不是第i行的最小数或者不是第j列的最大数则选择下一个元素继续。
显然矩阵A可以用一个二维数组表示
1 #define m 10 2 #define n 10 3 #define true 1 4 #define false 0 5 6 int saddle (int A[m][n]) 7 { 8 int count=0, i, j, k, rowmin, colmax; 9 10 for (i=0; i<m; i++) { 11 for (j=0; j<n; j++) { 12 k=0; 13 14 while (k<n && A[i][k] >= A[i][j]) { 15 k++; 16 } 17 18 if (k<n) { 19 rowmin = false; 20 } else { 21 rowmin = true; 22 } 23 24 if (rowmin == true) { 25 k=0; 26 27 while (k<m && A[k][j] <= A[i][j]) { 28 K++; 29 } 30 31 if (k<m) { 32 colmax = false; 33 } else { 34 colmax = true; 35 } 36 } 37 38 if (rowmin == true && colmax == true) { 39 count++; 40 } 41 } 42 } 43 44 return count; 45 }
双重循环体内有两个并列的while循环语句。第一个while循环执行O(n)次, 第二个while循环最多执行O(m)次。所以总的时间效率应该是O(m*n*(m+n))
除矩阵A用二维数组存储外, 用了几个辅加空间存储中间变量, 所以空间效率为O(1)
方法二:先把矩阵中每行最小数和每列最大数求出来,并分别存放在$line, $col两个一维数组中, 然后再对矩阵的每个元素aij进行比较, aij == $line[$i] == $col[$j]
1 /** 2 * A[$m][$n] 3 */ 4 function saddle() 5 { 6 //求每行的最小数 7 for ($i=0; $i < $m; $i++) { 8 $min = $A[$i][0]; 9 10 for ($j=1; $j < $n; $j++) { 11 if ($min > $A[$i][$j]) { 12 $min = $A[$i][$j]; 13 $line[$i] = $min; 14 } 15 } 16 } 17 18 19 //求每列的最大数 20 for ($i=0; $i < $n; $i++) { 21 $max = $A[0][$j]; 22 23 for ($j=1; $j < $m; $j++) { 24 if ($max < $A[$i][$j]) { 25 $max = $A[$i][$j]; 26 $line[$j] = $max; 27 } 28 } 29 } 30 31 32 //求所有鞍点 33 for ($i=0; $i < $m; $i++) { 34 for ($j=0; $j < $n; $j++) { 35 if ($line[$i] == $col[$j]) { 36 ++$count; 37 } 38 } 39 } 40 41 return $count; 42 }
该算法共有三小段并列的函数:
1. 求每行最小数的时间复杂度: O(m*n)
2. 求每列最大数的时间复杂度: O(m*n)
3. 统计所有鞍点的时间复杂度: O(m*n)
所以总的时间复杂度为: O(m*n)
很显然,空间复杂度为: O(m+n)