数据结构学习

本文详细解析了算法的时间复杂度概念,包括常数阶、线型阶、对数阶及平方阶,并通过具体代码示例说明如何计算不同类型的循环结构及函数调用的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

推导大O阶方法:

1:用常数1取代运行时间中的所有加法常数。

2:在修改后的运行次数函数中,只保留最高阶项。

3:如果最高阶项存在且不是1,则去除与这个项相乘的常数。

得到的结果就是大O阶。

一:常数阶:

例程:

int sum=0,n=100;       //执行一次

sum=(1+n)*n/2;         //执行一次

pritnf("%d",sum);        //执行一次

从例程来看,这个算法的运行次数函数是f(n)=3,根据推导方法,把常数项3改为1。在保留最高阶项时,我们发现它根本没有最高阶项,所以这个算法时间复杂度为O{1},常数阶。

试想一下,如果一个程序中有10次执行代码。

int sum=0,n=100;  //1

int sum=0,n=100;  //2

int sum=0,n=100;  //3

int sum=0,n=100;  //4

int sum=0,n=100;  //5

                .

                .

                .

int sum=0,n=100;  //10

事实上无论n为多少,上面的两个例程代码就是三次和十二次执行的差异,这与n的大小无关,执行时间恒定的算法,我们称之为具有O{1}的时间复杂度,又叫常数阶。

注意:无论常数为多少,我们都记为常数阶O{1},而不是O{3},O{12},等其他任何数字。单纯的分支结构也是常数阶。

二:线型阶

在确定线型阶的时候,我们需要确定某个特定语句或某个语句集运行次数,因此分析算法的复杂度,关键就是要分析循环结构的运行情况。

例程:循环的时间复杂度为O{n},因为循环体中的代码须要执行N次。

int i;

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

{

/*时间复杂度为O{1}的程序步骤序列*/

}

//确定线型阶时一定要看循环体中执行了几次

三:对数阶

例程

int count=1;

while(count<n)             //当count>=n时跳出while循环

{

count=count*2;

/*O{1}*/

}

这段循环,每次count乘以2之后,就距离n更近了一点,也就是说执行多少次乘2以后会count>=n,跳出循环。

不妨列一个代数式 :   2^x=n得到x=log2n。所以这个循环的时间复杂度为O{logn}。也叫做对数阶。

四:平方阶

举一个循环嵌套

int i,j;

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

{

for(j=0;j<n;j++)

{

//O{1}

}

}

在线型阶时我们已经分析了在循环中判断线型阶,所以这段循环嵌套的内层复杂度为O{n},在外层循环中又进行了n次循环,这个整体的循环嵌套的复杂度就为O{n^2}。

如果外循环的循环次数改为了m,那么时间复杂度就为O{m*n}。

由此我们可以总结得出,循环的时间复杂度等于循环体的复杂度乘以该循环运行的次数。

接下来我们再看一个例程:

int i,j;

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

{

for(j=i;j<n;j++)

{

//O{1}

}

}

这段例程中内层循环i=j; 由于当i=0时,内部循环执行n次,当i=j时,执行了n-1次,·········当i=n-1时,执行了一次。所以总执行次数为:

     n+(n-1)+(n-2)+······+1=n(n+1)/2=n^2/2+n/2

此时我们用文章开头提到的大O推导法,第一条没有加法常数不予考虑;第二条只保留最高阶项,因此我们保留n^2/2;第三条,去除这个项相乘的常数,就是去除1/2,最终我们得到这段代码的时间复杂度为O{n^2}。

接下来我们继续看,对于方法调用的时间复杂度又如何分析?

例程:

int i,j;

for(i=0;i<n;i++)       //O{n}

{

function(i);             //看函数体内的复杂度为什么

}

void function(int count)

{

print(count);                     //O{1}

}

//所以这段代码时间复杂度就为O{n}

函数体时打印这个参数,这就很好理解了,function函数的时间复杂度是O{1}。

假如在上段代码中function是下面这样的:

void function(int count)

{

int j;

for(j=count;j<n;j++)

{

//O{1}

}

}

事实上,这和刚才举的例子是一样的,只不过把嵌套内循环放在了函数中,所以最终时间复杂度还是O{n^2}。

举一个较为复杂的例子:

n++;                    //O{1}

function(n);              //O{n}

int i,j;

for(i=0;i<n;i++)           //O{n^2}

{

function(i);

}

for(i=0;i<n;i++)           //执行次数为n(n+1)/2

{

for(j=i;j<n;j++)

{

//O{1}

}

}

第三个注释里,这个嵌套循环我们已经分析过,时间复杂度为O{n^2},看看这段代码,根据大O推导法,最高阶项就是n^2所以这段代码的时间复杂度就为O{n^2}, 平方阶。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值