一、基本概念与术语
- 数据:能输入到计算机中并被识别处理的符合集合。分为数值数据和非数值数据。
- 数据项、数据元素、数据对象:例如,所有的学生档案为数据对象(具有相同性质的数据元素的集合),每一个学生的档案是一个数据元素(讨论数据时的最小单位),档案中的学号、姓名、性别等信息是数据项。
- 数据结构:数据之间的关系(类比CLASS类中的属性)和针对这些关系的基本操作算法(CLASS类中的方法)。数据之间的结构——(1)逻辑结构:集合、线性结构(一对一)、树结构(一对多)、图形结构(多对多)(2)存储结构:顺序存储结构和链接存储结构
- 数据类型→抽象数据类型
二、算法
- 算法:计算机求解特定问题的方法和步骤,是指令的有限序列。
- 算法的特性:输入(可以无输入)、输出(必须有输出)、有穷性、确定性(不存在二义性)、可行性
- 算法的评价:(1)正确性(对于合法输入,得出正确的结果)(2)健壮性(对错误的输入,能识别并作出处理)(3)可读性(通过模块化结构、加注释、命名有含义的变量、不用goto语句、不用全局变量等提高程序的可读性)(4)时间复杂性(执行时间短的算法效率高)(5)空间复杂性
三、算法设计
度量一个算法的效率,一种方法是事后统计,先将算法实现,并输入适当数据运行,测量其时间与空间的开销。
- 时间复杂度:(▲重点)
算法时间分析度量的标准不是针对实际执行时间精确算出算法执行的具体时间,而是针对算法语句中的执行次数(是与问题规模n有关的函数f(n))作出估评。
算法时间量度T(n)=O(f(n))——是数量级的概念
for(i=1;i<100;i++)
s++;
例1:语句执行次数是常量,T(n)=O(1)
for(i=1;i<n;i++)
s+=i;
例2:可以看到,这段程序中有 2 行代码,其中:for 循环从 i 的值为 0 一直逐增至 n(注意循环退出的时候 i 值为 n),因此 for 循环语句执行了 n+1 次;而循环内部仅有一条语句,s=s+i从 i 的值为 0 就开始执行,i 的值每增 1 该语句就再执行一次一直到 i 的值为 n-1,因此s+=i语句一共执行了 n 次。整段代码中所有语句共执行了 (n+1)+n 次,即 2n+1 次。
数据结构中,每条语句的执行次数,又被称为该语句的频度。整段代码的总执行次数,即整段代码的频度。
T(n)=O(f(n))=O(n)
for(i=0;i<n;i++) //n+1 注意:i加到n时退出
for(j=0;j<n;j++) //n(n+1)
s++; //n*n
例3:f(n)=n+1+n(n+1)+n*n T(n)=O(n*n)
MATRIXMLT(float A[n][n],float B[n][n],float C[n][n])//求两个矩阵的乘积
{ int i,j,k;
for(i=0;i<n;i++) //n+1 注意是i到n时运行完
for(j=0;j<n;j++) //n(n+1)
{ C[i][j]=0; //n*n 前面有两个循环
for( k=0;k<n;k++) //n*n*(n+1) 注意k也是到n+1运行完
C[i][j]+=A[i][k]*B[k][j] //n*n*n 前面有三个循环
}
}
例4:f(n)=2*n*n*n+3*n*n+2n+1 T(n)=O(n*n*n)
——常用的时间复杂度:
快速判断:与n无关直接就是常量阶O(1);有几个循环就是n的几次方;乘方就是开根号n;不断除/乘二就是取2的对数;
练习:
x=91; y=100;
while(y>0)
if(x>100)
{x=x-10;y--;}
else x++;//无论多么复杂没有出现n,T(n)=O(1)
i=1;
j=0;
while(i+j<n)
if(i>j)j++;
else i++
//只有一个while循环,用选择语句判断运算操作,T(n)=O(n)
i=1;
while(i<=n)
i=i*2; //T(n)=O(log2 n)
i=n;
while(i>0)
i=i/2; //T(n)=O(log2 n)
总结具体步骤:
⑴ 找出算法中的基本语句:算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体。
⑵ 计算基本语句的执行次数的数量级:只需计算基本语句执行次数的数量级,这就意味着只要保证基本语句执行次数的函数中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数。(简化算法分析,使注意力集中在最重要的增长率上)
⑶ 用大Ο记号表示算法的时间性能。
比较:c < log2n < n < n*log2n < n^2 < n^3 < 2^n < 3^n < n!
- 空间复杂度:算法的空间复杂度是指算法需要消耗的内存空间。每一个算法所编写的程序,运行过程中都需要占用大小不等的存储空间。例如程序代码本身所占用的存储空间;程序中如果需要输入输出数据,也会占用一定的存储空间;程序在运行过程中,可能还需要临时申请更多的存储空间。
如果程序所占用的存储空间和输入值无关,则该程序的空间复杂度就为 O(1);如果有关,则需要进一步判断它们之间的关系:如果随着输入值 n 的增大,程序申请的临时空间成线性增长,则程序的空间复杂度用 O(n) 表示;成 n的二次方关系增长,则程序的空间复杂度用 O(n2) 表示。
在多数场景中,一个好的算法往往更注重的是时间复杂度的比较,而空间复杂度只要在一个合理的范围内就可以。