时间复杂度和空间复杂度

1 概述

1)如何衡量一个算法(或一段程序)的性能?
通常我们通过该算法耗时长短以及耗费空间大小去衡量它的性能
2)那么如何确定算法的耗时长短以及耗费空间的大小呢?
a) 耗时长短:通常我们可以运行该算法程序,根据其运行时间的长短确定耗时,耗时少则性能好,但是由于不同计算机的cpu计算能力不同(当然还有其他很多因素,比如cpu时间片占用导致我们的算法等待),所以用这种算法也是不能准确计算出算法的耗时长短的,而且还有一个局限性就是采用这种计算耗时的方法必须是写完算法之后;所以为了在设计算法之前就对算法耗时能有初步的分析和估算以便更好的设计算法,大佬们总结了一个方法,就是我们后面要说的时间复杂度。
b) 耗费空间大小:如果说要确定一个算法的具体耗费计算机内存空间的大小,那我认为比确定耗时可繁琐多了,我们要根据不同的数据类型确定它具体占多少个字节,如果是对象,我们又得考虑其内部的属性,以及对象头等乱七八糟的东西,如果算法比较复杂,涉及的变量又非常多,那肯定头大了,而且个人认为得到的具体的占用内存大小值与投入的精力相比就变得略显苍白;当然,前辈们也总结了一个估算方法帮助我们更好的设计算法,就是空间复杂度。

2 大O表示法

在讲时间复杂度和空间复杂度之前,这里先补充一下大O数学知识点

大O符号(英语:Big O notation)是用于描述函数渐近行为的数学符号。更确切地说,它是用另一个(通常更简单的)函数来描述一个函数数量级的渐近上界。在数学中,它一般用来刻画被截断的无穷级数尤其是渐近级数的剩余项;在计算机科学中,它在分析算法复杂性的方面非常有用

给定两正值函数fg, 定义:
如果存在正实数cN,使得对于所有n≥N, 使得|f(n)| ≤ |cg(n)| ,则可以表示为f(n)=O(g(n))

大O表示小于等于,比如,f(n)=O(g(n))我们可以理解为f(n) ≤ g(n),也可以理解为g(n)是f(n)的渐近上界

本小节参考博文:
算法分析:大O符号/大Ω符号/大Θ符号/小o符号/小w符号

3 时间复杂度

时间频度:前辈Knuth认为一个算法运行的总时间主要和两点有关:①执行每条语句的耗时;②执行每条语句的频率。前者主要取决于计算机、编译器和操作系统,后者主要取决于程序本身和输入。我们忽略计算机等影响因素,假定执行每条语句耗时相等,从而可以得出,一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度,记作T(n),其中n为问题规模。

时间复杂度:一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f (n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。时间复杂度就是用来衡量算法运行时间的,f(n)的增长量级越大,说明随着问题规模的增加,算法的运行时间增长越快,换言之就是,f(n)的增长量级越大,算法越耗时,性能越差,比如O(n^2) 和O(n) 相比,时间复杂度为O(n)的算法性能越好。

我们一般关注的是最坏时间复杂度,即在最坏的情况下分析算法中语句执行的次数上界
3.1 那么如何分析并确定一个算法的时间复杂度呢?
3.1.1 步骤:
⑴ 找出算法中的基本语句;
算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体。
⑵ 计算基本语句的执行次数的数量级;
只需计算基本语句执行次数的数量级,这就意味着只要保证基本语句执行次数的函数中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数。这样能够简化算法分析,并且使注意力集中在最重要的一点上:增长率
⑶ 用大O记号表示算法的时间性能。
将基本语句执行次数的数量级放入大O记号中。
3.1.2 举例说明

① 常数阶O(1)

1 int a = 1;
2 int b = a;
3 int c = 2;

以上三行代码的语句频度均为1,该算法片段的执行时间是一个与问题规模n无关的常数。算法的时间复杂度为常数阶,记作T(n)=O(1)。注意:如果算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数。此类算法的时间复杂度是O(1)。

② 对数阶O(logn)

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

语句3为执行次数最多的基本语句,时间频度为f(n)
2^f(n) <= n =>
f(n) <= log2n =>
时间复杂度为:T(n) = O(f(n)) = log2n

③ 线性阶O(n)

1 int sum = 0;
2 for(int i = 1; i<=n; i++) {
3    sum++;
4 }
5 System.out.println(sum);

第三行代码就是执行次数最多的基本语句,时间频度为n,其他语句的频度可以忽略,故时间复杂度为T(n) = O(n),表示随着问题规模n的增加,算法运行时间呈线性增长。

④ 线性对数阶O(nlogn)

1 for(int i = 0; i<=n; i++) {
2   int x = 1;
3   while(x < n) {
4      x = x * 2;
5   }
6 }

语句4就是执行次数最多的语句,时间频度为n*log2n,所以时间复杂度为T(n) = O(nlogn)

⑤ 平方阶O(n2)

1 int count = 0;
2 for(int i = 1;i <= n;i++){
3    for(int j = 1;j <= n;j++) {
4       count++;
5    }
6 }

第四行代码就是执行次数最多的基本语句,时间频度为n*n=n2,所以时间复杂度为T(n) = O(n2)

⑥ 立方阶O(n3)
⑦ 指数阶O(2n)
⑧ 阶乘阶O(n!)

3.1.3 常见的算法时间复杂度由小到大依次为:
O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<…<O(2n)<O(n!)

4 空间复杂度

类似于时间复杂度的讨论,一个算法的空间复杂度(Space Complexity)S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。渐近空间复杂度也常常简称为空间复杂度。
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。
一个算法在计算机存储器上所占用的存储空间,包括存储算法本身所占用的存储空间算法的输入输出数据所占用的存储空间算法在运行过程中临时占用的存储空间这三个方面。
算法的输入输出数据所占用的存储空间是由要解决的问题决定的,是通过参数表由调用函数传递而来的,它不随本算法的不同而改变。
存储算法本身所占用的存储空间与算法书写的长短成正比,要压缩这方面的存储空间,就必须编写出较短的算法。
算法在运行过程中临时占用的存储空间随算法的不同而异,有的算法只需要占用少量的临时工作单元,而且不随问题规模的大小而改变,我们称这种算法是“就地"进行的,是节省存储的算法;有的算法需要占用的临时工作单元数与解决问题的规模n有关,它随着n的增大而增大,当n较大时,将占用较多的存储单元。
当一个算法的空间复杂度为一个常量,即不随被处理数据量n的大小而改变时,可表示为O(1);当一个算法的空间复杂度与以2为底的n的对数成正比时,可表示为0(10g2n);当一个算法的空间复杂度与n成线性比例关系时,可表示为0(n).若形参为数组,则只需要为它分配一个存储由实参传送来的一个地址指针的空间,即一个机器字长空间;若形参为引用方式,则也只需要为其分配存储一个地址的空间,用它来存储对应实参变量的地址,以便由系统自动引用实参变量。

算法的空间复杂度通过计算算法所需的存储空间实现,算法空间复杂度的计算公式记作:S(n)= O(f(n)),其中,n为问题的规模,f(n)为语句关于n所占存储空间的函数。

一般情况下,一个程序在机器上执行时,除了需要存储程序本身的指令、常数、变量和输入数据外,还需要存储对数据操作的存储单元。若输入数据所占空间只取决于问题本身,和算法无关,这样只需要分析该算法在实现时所需的辅助单元即可。若算法执行时所需的辅助空间相对于输入数据量而言是个常数,则称此算法为原地工作,空间复杂度为O(1)。关于O(1)的问题, O(1)是说数据规模和临时变量数目无关,并不是说仅仅定义一个临时变量。举例:无论数据规模多大,我都定义100个变量,这就叫做数据规模和临时变量数目无关。就是说空间复杂度是O(1)。

对于一个算法,其时间复杂度和空间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。另外,算法的所有性能之间都存在着或多或少的相互影响。因此,当设计一个算法(特别是大型算法)时,要综合考虑算法的各项性能,算法的使用频率,算法处理的数据量的大小,算法描述语言的特性,算法运行的机器系统环境等各方面因素,才能够设计出比较好的算法。

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

在这里插入图片描述
一个经验规则:其中c是一个常量,如果一个算法的复杂度为c 、 log2n 、n 、 n*log2n ,那么这个算法时间效率比较高 ,如果是2n ,3n ,n!,那么稍微大一些的n就会令这个算法不能动了,居于中间的几个则差强人意。
算法复杂度分析是一个很重要的问题,任何一个程序员都应该熟练掌握其概念和基本方法,而且要善于从数学层面上探寻其本质,才能准确理解其内涵。

5 参考资料

百度百科时间复杂度
百度百科空间复杂度
关于计算时间复杂度和空间复杂度

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值