学习笔记(一)
算法复杂度(Algorithmic Complexity)
- 同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。一个算法的评价主要从时间复杂度和空间复杂度来考虑
时间复杂度
- 一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度,记为T(n)。算法的时间复杂度是指执行算法所需要的计算工作量。(****换句话说,时间复杂度指算法语句的执行次数。一个算法语句的执行次数最终都是可以通过函数f(n)来表示的
- )再纠正一个误区,算法不单单指冒泡排序之类的,一个循环甚至是一个判断都可以称之为算法
- 在刚才提到的时间频度中,n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但有时我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),存在一个正常数c使得f(n)c>=T(n)恒成立。
记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1),另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n**2+3n+4与T(n) = 4n**2+2n+1它们的频度不同,但时间复杂度相同,都为O(n^2)。
按数量级递增排列,常见的时间复杂度有:
常数阶O(1),
对数阶O(log2n)(以2为底n的对数,下同),
线性阶O(n),
线性对数阶O(nlog2n),
平方阶O(n^2),
立方阶O(n^3),…,
k次方阶O(n^k),
指数阶O(2^n)。
随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
#define n 100
void matrix_multiply(ing A[n][n],int B[n][n],int C[n][n])
{
int i,j,k;
for(i=0;i<n;i++) //这条语句运行n+1次
{
for(j=0;j<n;j++) //这条语句运行n*(n+1)次
{
C[i][j] = 0; //这条语句运行n**2次
for(k=0;k<n;k++) //这条语句运行(n+1)*n*n次
{
C[i][j] = C[i][j] + A[i][k]*B[k][j]; //这条语句运行n**3次
}
}
}
}
该算法中所有语句的频度之和(即算法的时间耗费)为
所以T(n) = 2n3 + 3*n2 + 2n+1
当n趋向无穷大时,显然有T(n)~O(n^3);
这表明,当n充分大时,T(n)和n3之比是一个不等于零的常数。即T(n)和n3是同阶的,或者说T(n)和n**3的数量级相同。记作T(n)=O(n^3)是矩阵乘法的渐近时间复杂度。
空间复杂度:
是对一个算法在运行过程中临时占用存储空间的度量,一个算法在计算机存储器上所占用的存储空间包括存储算法本身所占用的空间,算数和输入输出所占用的存储空间以及临时占用存储空间三个部分,算法的输入输出数据所占用的存储空间是由待解决的问题来决定的,通过参数表由调用函数而来,它随本算法的不同而改变,存储算法本身所占用的存储空间有算法的书写长短成正比。算法在运行过程中占用的临时空间由不同的算法决定。
当有若干个循环时,时间复杂度是和嵌套层数最多的循环语句中最内层的频度决定的
for (i = 0; i < n; i++)
{
for (j = 0; j < i; j++)
{
for (k = 0; k < j; k++)
{
x++;
}
}
}
时间复杂度:O(n^3),空间复杂度:O(1)
二分法
int select(int a[], int k, int len)
{
int left = 0;
int right = len - 1;
while (left <= right)
{
int mid = left + ((right - left) >> 2);
if (a[mid] == k)
{
return 1;
}
else if (a[mid] > k)
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
return NULL;
}
在最坏的情况下循环x次后找到,n/(2^x)=1;x=log2n;
所以二分查找的时间复杂度为:O(log2n);空间复杂度O(1);
用递归算法求n的阶乘
int fac(int n)
{
if (n <= 2)
{
return n;
}
else
{
return fac(n - 1)*n;
}
}
n的阶乘的时间复杂度很简单:就是n次递归算法,所以为O(n),空间复杂度O(n),递归的深度是n。
斐波那契数列
int fib(int n)
{
if (n < 2)
{
return 1;
}
else
{
return fib(n-1) + fib(n - 2);
}
}
将其用流程图画出来发现是一个完全二叉树,节点数为2n-1,深度为n,根据最坏情况,时间复杂度为O(2n),空间复杂度为O(n);