数据结构时间复杂度讲解与练习
一个算法中的语句执行次数称为语句频度或时间频度,记为T(n)。n称为问题的规模,当n不断变化时,时间频度T(n)也会不断变化。但是有时候,我们想知道它变化时呈现什么规律。为此,我们引入时间复杂度概念。
一般情况下,算法中基本操作重复执行的次数,是问题规模 n 的某个函数,用T(n)表示。若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度。
时间频度不相同时,渐进时间复杂度O(f(n)) 有可能相同,如T(n)=n^2+3n+4与T(n)=4n^2+2n+1它们的频度不同,但时间复杂度相同,都为O(n^2)。
现在我们根据一些书本上和网络上对时间复杂度概念的描述进行一下总结:
T(n),语句频度,时间频度,亦称为时间复杂度。
O(f(n)),渐进时间复杂度。
前者T(n)是某个算法的时间耗费,它是该算法所求解 问题规模 n的函数,而后者O(f(n))是指当问题规模趋向无穷大时,该算法时间复杂度的数量级。当我们评价一个算法的时间性能时,主要标准就是算法的渐近时间复杂度O(f(n)),因此,在算法分析时,往往对两者不予区分,经常是将渐近时间复杂度T(n)=O(f(n))简称为时间复杂度,其中的f(n)一般是算法中频度最大的语句频度。
注意:算法中语句的频度不仅与问题规模有关,还与输入实例中各元素的取值相关。但是我们总是考虑在最坏的情况下的时间复杂度。以保证算法的运行时间不会比它更长。
常见的时间复杂度,按数量级递增排列依次为:常数阶O(1)、对数阶O(log2n)或O(lbn)、线性阶O(n)、线性对数阶O(n*log2n)、平方阶O(n^2)、立方阶O(n^3)、k次方阶O(n^k)、指数阶O(2^n)。
· 下面有一道题目是可以帮助同学们理解概念的:
1、设三个函数f,g,h分别为 f(n)=100*n^3+n^2+1000 , g(n)=25*n^3+5000*n^2 , h(n)=n^1.5+5000*n*lgn
请判断下列关系是否成立:
(1) f(n)=O(g(n))
(2) g(n)=O(f(n))
(3) h(n)=O(n^1.5)
(4) h(n)=O(nlgn)
这里我们复习一下渐近时间复杂度的表示法T(n)=O(f(n)),这里的"O"是数学符号,它的严格定义是 "若T(n)和f(n)是定义在正整数集合上的两个函数,则T(n)=O(f(n))表示存在正常数C和n0 ,使得当n≥n0时都满足0≤T(n)≤C*f(n)。"(即书本中的定义)。通俗一点就是这两个函数当整型自变量n趋向于无穷大时,两者的比值是一个不等于0的常数。
◆ (1)成立。题中由于两个函数的最高次项都是n^3,因此当n→∞时,两个函数的比值是一个常数,所以这个关系式是成立的。
◆ (2)成立。与上同理。
◆ (3)成立。与上同理。
◆ (4)不成立。由于当n→∞时n^1.5比n*lgn递增的快,所以h(n)与nlgn的比值不是常数,故不成立。
理解完概念之后,就开始求算法的时间复杂度。
从概念中我们知道,要求时间复杂度O(f(n)),就必须要知道算法中频度最大的语句频度f(n),那么要求最大的语句频度f(n)就必须要知道算法的语句频度T(n)。一般总的思路就是:T(n)->f(n)->O(f(n))。有时候可以直接找到算法中频度最大的语句,直接算出f(n),然后写出O(f(n))。也有例外情况就是很难求出语句频度T(n)的
下面会用一些例子做详细的说明:O(1)
例1:Temp=i;i=j;j=temp;
以上三条单个语句的频度均为1,该程序段的执行时间是一个与问题规模n无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1)。如果算法的执行时 间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1)。
例2:x=91; y=100;
while(y>0)
if(x>100)
{x=x-10;y--;}
else x++;
解答: T(n)=O(1), 这个程序看起来有点吓人,总共循环运行了1000次,但是我们看到n没有? 没。这段程序的运行是和n无关的,就算它再循环一万年,我们也不管他,只是一个常数阶的函数。
O(n)
例1:
i=1; k=0
while(i<n)
{ k=k+10*i;i++;
}
解答:T(n)=n-1, T(n)=O(n), 这个函数是按线性阶递增的。
例2:
a=0;
b=1; ①
for (i=1;i<=n;i++) ②
{
s=a+b; ③
b=a; ④
a=s; ⑤
}
解: 语句1的频度:2,
语句2的频度: n,
语句3的频度: n-1,
语句4的频度:n-1,
语句5的频度:n-1,
T(n)=2+n+3(n-1)=4n-1=O(n)
O(n^2)
例1: 交换i和j的内容
sum=0; (一次)
for(i=1;i<=n;i++) (n次 )
for(j=1;j<=n;j++) (n^2次 )
sum++; (n^2次 ) (此语句是算法中频度最大的,可直接算出f(n)=n^2,得时间复杂度T(n)=O(n^2))
解:T(n)=2n^2+n+1 =O(n^2)
例2:
for (i=1;i<n;i++)
{
y=y+1; ①
for (j=0;j<=(2*n);j++)
x++; ②
}
解: 语句1的频度是n-1
语句2的频度是(n-1)*(2n+1)=2n^2-n-1
f(n)=2n^2-n-1+(n-1)=2n^2-2
该程序的时间复杂度T(n)=O(n^2).
O(n^3)
例1:
for(i=0;i<n;i++)
{
for(j=0;j<i;j++)
{
for(k=0;k<j;k++)
x=x+2; (此语句为频度最大的语句,可算出f(n)=n^3,写出时间复杂度T(n)=O(n^3))
}
}
解:当i=m, j=k的时候,内层循环的次数为k当i=m时, j 可以取 0,1,...,m-1 , 所以这里最内循环共进行了0+1+...+m-1=(m-1)m/2次所以,i从0取到n, 则循环共进行了: 0+(1-1)*1/2+...+(n-1)n/2=n(n+1)(n-1)/6所以时间复杂度为O(n^3).
O(log2n ) (此为例外情况,难以算出T(n),但可以利用技巧直接算出O(f(n))。)
例1:
i=1; ①
while (i<=n)
i=i*2; ②
解: 语句1的频度是1,
设语句2的频度是f(n), 则:2^f(n)<=n;f(n)<=log2n
取最大值f(n)= log2n,
T(n)=O(log2n )
有如下复杂度关系
c < log2N < n < n * Log2N < n^2 < n^3 < 2^n < 3^n < n!
其中c是一个常量,如果一个算法的复杂度为c 、 log2N 、n 、 n*log2N ,那么这个算法时间效率比较高 ,如果是 2^n , 3^n ,n!,那么稍微大一些的n就会令这个算法不能动了,居于中间的几个则差强人意。
1.、算法应该是( B)。
A.程序 B.问题求解步骤的描述 C.要满足五个基本特性
D. A和C
【解析】:程序不一定满足有穷性,如死循环、操作系统等,而算法必须有穷。算法代表了对问题求解步骤的描述,而程序则是算法在计算机上的特定的实现。
2.某算法的时间复杂度为O(n2),表明该算法的( C)。
A.问题规模是n2 B.执行时间等于n2
C.执行时间与n2成正比
D.问题规模与n2成正比
【解析】:时间复杂度为O(n2),说明算法的执行时间T(n)<=c * n2(c为比例常数),即T(n)=O(n2),时间复杂度T(n)是问题规模n的函数,其问题规模仍然是n而不是n2。
3.以下算法的时间复杂度为( D)。
void fun(int n) {
int i=l;
while(i<=n)
i=i*2;
}
A. O(n)
B. O(n2) C. O(nlog2n) D. O(log2n)
【解析】基本运算是i=i*2,设其执行时间为T(n),则2T(n)<=n,即T(n)<=log2n=O(log2n)。
4.【2011年计算机联考真题】
设n是描述问题规模的非负整数,下面程序片段的时间复杂度是(A)。
x=2;
while(x<n/2)
x=2*x;
A. O(log2n)
B. O(n) C. O(nlog2n) D. O(n2)
【解析】:在程序中,执行频率最高的语句为“x=2*x”。设该语句共执行了
t次,则2t+1=n/2,故t=log2(n/2)-1=log2n-2,得 T(n)=O(log2n)。
5.【2012年计算机联考真题】
求整数n (n>=0)阶乘的算法如下,其时间复杂度是( B)。
int fact(int n){
if (n<=l) return 1;
return n*fact(n-1);
}
A. O(log2n)
B. O(n) C. O(nlog2n) D. O(n2)
【解析】:本题是求阶乘n!的递归代码,即n*(n-1)*...*1共执行n次乘法操作,故T(n)=O(n)
6.有以下算法,其时间复杂度为( )。
void fun (int n){
int i=0;
while(i*i*i<=n)
i++;
}
7.程序段
for(i=n-l;i>l;i--)
for(j=1;j<i;j++)
if (A[j]>A[j+l])
A[j]与 A[j+1]对换;
其中n为正整数,则最后一行的语句频度在最坏情况下是(D)。
A. O(n) B. O(nlogn) C.
O(n3) D. O(n2)
【解析】:当所有相邻元素都为逆序时,则最后一行的语句每次都会执行。此时,
8.以下算法中加下划线语句的执行次数为(A)。
int m=0, i, j;
for(i=l;i<=n;i++)
for(j=1;j<=2 * i;j++)
m++;
A.
n(n+1) B. n C. n+1 D. n2
【解析】:
9.下面说法错误的是( B)。
Ⅰ.算法原地工作的含义是指不需要任何额外的辅助空间
Ⅱ.在相同的规模n下,复杂度O(n)的算法在时间上总是优于复杂度O(2n)的算法
Ⅲ.所谓时间复杂度是指最坏情况下,估算算法执行时间的一个上界
Ⅳ.同一个算法,实现语言的级别越高,执行效率就越低
A. Ⅰ B. Ⅰ、Ⅱ C. Ⅰ、Ⅳ
D. Ⅲ
【解析】:Ⅰ,算法原地工作是指算法所需的辅助空间是常量。Ⅱ,题中是指算法的时间复杂度,不要想当然认为是程序(该算法的实现)的具体执行时间,而赋予n—个特殊的值。时间复杂度为O(n)的算法,必然总是优于时间复杂度为O(2n)的算法。Ⅲ,时间复杂度总是考虑在最坏情况下的时间复杂度,以保证算法的运行时间不会比它更长。Ⅳ为严蔚敏教材的原话。
二、综合应用题
2.分析以下各程序段,求出算法的时间复杂度。
// 程序段①
i=l;k=0;
while(i<n-l)
{
k=k+10*i;
i++;
}
【解析】:基本语句是k=k+10*i,共执行了n-2次,所以T(n)=O(n)。
// 程序段②
y=0;
while((y+1)*(y+1)<=n)
y=y+1;
【解析】:设循环体共执行T(n)次,每循环一次,循环变量y加1,最终T(n)=y。故(T(n))2<=n,解得 T(n)=O(n1/2)。
// 程序段③
for(i=l;i<=n;i++)
for(j =1;j <=i;j ++)
for(k=l;k<=j;k++)
x++;
【解析】:
// 程序段④
for(i=0;i<n;i++)
for(j=0;j<m;j++)
a[i] [j]=0;
【解析】:a[i][j]=0是基本语句,内循环执行m次,外循环执行n次,共执行了 m*n次,所以 T(m, n)=O(m*n)0