算法的时间复杂度和空间复杂度

你在北方的寒夜里四季如春,我在南方的艳阳里冻成傻逼,作为一名刚从北方放假回到南方的学生党,此时感觉受到了100t的伤害~(咳咳~不扯了,我在被窝的远方瑟瑟发抖的掏出小手写的这波帖子,求安(dian)慰(zan))。

书归正传哈,今天咱们要讲的就是算法的时间复杂度和空间复杂度,众所周知,算法是一个程序的灵魂,程序写的好与坏,关键在于它的执行效率要快,所占内存空间要小,因此要想对于某个问题得到最有效的算法,我们首先要去了解什么是算法的时间复杂度和空间复杂度,和如何计算一个算法的时间复杂度,并且如何优化。(传说中的终极三大步骤。是啥,为啥,该咋办)

一、算法的时间复杂度

1.什么是算法的时间复杂度

算法的时间复杂度就是在进行算法分析时,语句的总执行次数我们记为T(n),它是关于问题规模n的一个函数,我们就是分析T(n)随n变化的情况以及确定n的数量级。我们把算法的时间复杂度T(n)来进行算法的时间度量,记作T(n)=O(f(n))。其实f(n)是算法的准确时间复杂函数,但由于我们现实生活中,n一般非常大,所以我们不用考虑具体的函数形式(函数的渐进增长,因此判断一个算法的效率,更关注的应该是最高阶的阶数),而只需要去考虑其增长率就可以了,所以我们又把T(n)叫做算法的渐进时间复杂度,简称为时间复杂度。这样用O()来表示时间复杂度的记法,我们把它叫做大O表示法。

我们如何去寻求关于一个问题最有效率的算法呢,其实就是去寻求当n增长时,T(n)增长最慢的算法(你也可以看成一阶导=。=)。

2.如何推导大O阶方法

上面咱们已经讲过什么是O表示法,那么问题来了,对于一个n规模的算法,如何求解其大O表示法呢,我只简单谈一谈我的经验:

①得到关于问题的f(n)函数,并只保留最高阶

②用常数1来替代所有的系数常数

大O表示法得到的可分为常数阶、线性阶、对数阶、平方阶、立方阶、指数阶、阶乘阶等等,因为在正常开发中我们的n的规模都会很大,因此像立方阶以上的算法我们基本不予考虑,所以大家在设计算法时尽量不要选择平方阶以上的算法,因此我们只介绍常数阶、线性阶、对数阶以及平方阶。(福利:常用的时间复杂度所消耗的时间从小到大排序为O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n))

3.常数阶

常数阶的表示方法为O(1),下面举个非常简单的例子:

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

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

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

大家按照我的计算方式:首先得到f(n)=3,最高阶就是常数阶3,第二步,把常数变为1,就得到了此算法的算法复杂度为O(1),是不是灰常简单,其实其它阶的也可以这么算,不过对于有循环的,大家可以采取更加简便的方法进行运算(口算),预知详情,请往下看~

4.线性阶

什么叫做线性阶呢?其实就是一个f(n)是一个一元一次的函数,对应的大O阶为O(n),下面再举个例子:

int i;

for(int i = 0;i < n;i++){//n次

//时间复杂度为O(1)的程序步骤序列

}

上方的计算仍然可以按照我们的计算方法,非常简单的就能够算出来其为O(n),大家可能有疑问的就是时间复杂度为O(1)运算n次为啥就是O(n),其实非常简单,就是你想把它看成几就看成几,反正就是一个常数(不随n变化而发生变化的数),按照我们提供的方法,运算出来仍然为O(n),所以我们运算得时候就可以吧O(1)看成1,其它也如法炮制。

5.对数阶

给出下面一段代码,求其时间复杂度:

int i = 1;

while(i < n){

i = i*2;

}

这段代码非常简单,但有很多初学的同学会犯错误,就是看到有循环,没有嵌套,其就是O(n),这种算法是错误的,按照咱们的方法:先算f(n),2^x=n,f(n)=x=logn(有个以2为底,抱歉,打不出来=。=,大家明白什么意思就可以了),所以它复杂度为O(logn)

6.平方阶

平方阶O(n^2),其出现必然会有一个必要条件,就是循环嵌套:

特别简单的例子就不举了,举一个稍微复杂一丢丢丢丢丢的:

void function(int count){

int j;

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

//时间复杂度为O(1)的程序序列

}

}

对于这段代码:

n++;//1

function(n);//n

int i,j;

for(i = 0;i < n;i++){//n*(n+1)/2

function(n);

}

for(i = 0;i < n;i++){//n*(n+1)/2

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

//时间复杂度为O(1)的程序序列

}

}

f(n) = 1+n*(n+1) so T(n)=O(n^2)

时间复杂度小结:我们所求的时间复杂度均为最坏时间复杂度,其实我们所期望的复杂度是平均复杂度,也就是所谓的平均时间,但现实中数学期望需要建立合适的数学模型去求解,一般的现实问题很难通过分析得到,所以,一般没有特殊说明的话,我们所求的就是最坏时间复杂度。

二、算法的空间复杂度

其实我们写算法时,往往时间复杂度和空间复杂度是有一定关系的,往往时间复杂度较少的算法可能所占用的空间(也就是空间复杂度要稍微高一些)。举个简单的例子,就是我们在学c语言时都接触过算闰年的算法,如果我们选取一个非常大的数组来存储各个年份,闰年记为1,不是记为0,那么我们的时间复杂度肯定为常数阶,但是可取么,因为我们浪费的空间非常大(也就是空间复杂度很大),所以,怎样去选择合适的算法,需要综合考虑。

算法的空间复杂度需要通过计算算法所需的存储空间来实现,其公式记为S(n)=O(f(n)),f(n)为关于n的所占存储空间的函数。

一般情况下,一个程序在机器上执行的时候,除需要存储程序本身的指令、常数、变量和输入数据之外,还要存储对数据操作的存储单元。若输入数据所占空间与算法本身没有关系,只取决于问题的本身,此时,只需要分析算法在实现的时候的的辅助单元所占用的存储空间即可,要是其辅助空间对于输入数据量而言是个常数,此算法成为原地工作,空间复杂度为O(1)。

一般的呢,我们用时间复杂度来度量运行时间的需求,空间复杂度来说明存储空间的需求。当然,我们计算一个算法的效率时重点还是要考虑时间复杂度的问题(从我写的篇幅就能看出来),祝大家代码快乐,回被窝睡觉了...喜欢记得点赞留言转发哦,不对的地方欢迎大家批评指正~~~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值