复杂度分析

度量一个程序的执行时间一般有两种方法:

1.事后统计法:计算机内部有计时功能甚至可以精确到毫秒级别。

但这种方法有两个明显的缺陷:

(1)必须先运行程序

(2)依赖计算机硬件,软件等环境因素,容易遮盖算法本身的优劣

(ps:测试结果受数据规模的影响很大,比如:对同一个排序算法,待排序数据的有序度不一样,排序的执行时间会有很大的差别,极端情况下,若数据已经是有序的,那么排序算法不需要做任何操作,执行时间会非常短,其次,若数据规模太小,测试结果也无法真实地反映算法的性能,比如,对于小规模数据,插入排序可能比快速排序要快。)

2.事前分析估算法:取决于以下因素——>

(1)一句的算法用何种策略

(2)问题的规模

(3)书写程序的语言

(4)编译程序所产生的机器代码质量

(5)机器执行指令的速度

以上都是课本上的内容,接下来大概讲讲如何对算法进行粗略的计算:

int main(){

int i=0;

int sum=0;

for(i=0;i<n;i++){

    cin>>j;

    sum+=j;
}
}

第二三行代码分别需要一个unit_time的执行时间,第四五六行都运行了n遍,素以需要3n*unit_time的执行时间,所以这段代码的总执行时间是(3n+2)*unit_time

int main(){

int sum=0;

int i=1;

int j=1;

for(:i<=n;i++){

    j=1;

    for(:j<=n;j++){

        sum=sum+i*j;
}
}
}

第二三四行代码,每行只需要一个unit_time 即可,而第五六行代码循环执行了n遍,需要2n*unit_time的执行时间,第七八行代码循环执行了n^2遍,所以需要2n^2*unit_time的执行时间。整段代码的总执行时间是T(n)=(2n^2+2n+3)*uxit_time

由此可得每行代码的执行时间与执行次数成正比。

当n很大时,可以当成100000,10000000,而书上的公式中的低阶,常量,系数三部分并不左右增长趋势,所以可以忽略,我们只需要关注最大量级就可以了,因此,上两例中的时间复杂度为:T(n)=O(n), T(n)=O(n^2)

以下有三个比较实用的方法:

  1. 只关注循环执行次数最多的一段代码

第一个例子中,第二三行代码都时常量级的执行时间,与n无关,所以对时间复杂度无影响,可忽略。循环执行次数最多的是第四五六行代码,所以重点分析这块代码,被执行了n次,因此总的时间复杂度为O(n)。

2.加法法则:总复杂度等于量级最大的那段代码的复杂度

int cal(int n){
int sum_1=0;
int p=1;
for(;p<100;++p){
    sum_1=sum_1+p;}
}


int sum_2=0;
int q=1;
for(;q<n;++q){
    sum_2=sum_2+q;}

int sum_3=0;
int  i=1;
int  j=1;
for(;i<n;++i){
    j=1;
    for(;j<=n;++j):
        sum_3=sum_3+i*j;
}
}

这个代码分三部分,分别求sum1,sum2,sum3.我们分别分析每一部分的时间复杂度,再取一个量级最大的作为整段代码的复杂度。

Sum1这段代码被执行了100次,所以是一个常量级,与n的规模无关

(ps:即便这段代码被执行了10000000次,只要次数已知,跟n无关,照样是常量级的执行时间,可以忽略)

第二三段代码的时间复杂度是O(n)和O(n^2)。

综合这三段代码的时间复杂度,取其中最大量级n^2,所以整段代码的时间复杂度为O(n^2).

也就是说,总的时间复杂度就等于量级最大的那段代码的时间复杂度。

3.乘法法则:嵌套代码的复杂度等于嵌套内外代码复杂度的乘积

int cal(int n){
int ret=0;
int i=0;
for(;i<n;++i{
ret+=f(i);}
}


int f(int n){
int sum=0;
int i=1;
for(;i<n;++i){
sum+i;}
return sum;}

集中常见时间复杂度:

O(1), O(logn),O(n),O(nlogn),O(n^2),O(n^3)

  1. O(1)
int i=4;
int j=3;
int k=i+j;

O(1)是常量级时间复杂度的一种表示方法。而不是说只执行了一行代码,所以这段代码,计时有三行,但时间复杂度为O(1),而不是O(3)。

一般情况下,只要算法中不含循环,递归语句,即使有成千上万行代码,它的时间复杂度都是O(1)。

2.O(logn),O(nlogn)

对数阶时间复杂度非常常见,如:

i=1;
while(i<=n){
i=i*2;}

i的取值为2,4,8......,(2^x)<=n;x等于log2(n) [2为底],所以这段代码的时间复杂度为O(log2(n));

再看下列代码

i=1;
while(i<=n){
i=i*3;}

i的取值为1,3,9,27,81.....(3^x)<=n,这段代码的时间复杂度为O(log3(n)),

实际上,不管是2,3还是别的数为底,我们可以把对数阶的时间复杂度都记为O(logn),

因为对数之间是可以相互转换的,log3(n)+log3(2)*log2(n),所以O(log3(n)=O(C*log2(n)),其中log3(2)是常量,可忽略,所以对数阶复杂度可表示为O(logn).

O(nlogn)就很容易啦,就是将上述代码嵌在一个执行n次的循环中就好了。

3.O(m+n),O(m*n)

int cal(int m,int n){
int sum_1=0;
int i=1;
for(;i<m;++i){
sum_1=sum1+i;}


int sum_2=0;
int j=1;
for(;j<n;++j){
sum_2+=j;}

return sum_1+sum_2;}

由于m,n两个数据规模未知,因此不能简单运用加法法则省略掉其中一个,所以代码的时间复杂度为O(m+n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值