【数据结构】之时间复杂度和空间复杂度

1.时间复杂度和空间复杂度的定义

时间复杂度和空间复杂度是用来评价算法效率高低的2个标准;

时间复杂度:也称渐进时间复杂度,它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,算法复杂度分析的实际是一种时间增长的局势,通俗来说就是计算算法执行所需要的次数。

空间复杂度:是对一个算法在运行过程中临时中占用的存储空间大小的量度。

2.时间复杂度计算

2.1推导大O阶方法

分析一个算法的时间复杂度步骤:

1.用常数1取代运行时间中的所有加法常数。
2.在修改后的运行次数函数中,只保留最高阶项。
3.如果最高阶项存在且不是1,则去除与这个项相乘的常数。
4.得到的最后结果就是大O阶。

(1)常数阶
例子:


int sum = 0, n = 100;
printf(“I love you.com\n”);
printf(“I love you.com\n”);
printf(“I love you.com\n”);
printf(“I love you.com\n”);
printf(“I love you.com\n”);
printf(“I love you.com\n”);
sum = (1+n)*n/2;

很明显每一个print执行下来时间复杂度都是O(1)。

(2)线性阶:一般含有非嵌套循环涉及线性阶,线性阶就是随着问题规模n的扩大,对应计算次数呈直线增长。


int i , n = 100, sum = 0;
for( i=0; i < n; i++ )
{
    sum = sum + i;
}

上面这段代码,它的循环的时间复杂度为O(n),因为循环体中的代码需要执行n次。

(3)平方阶

int i, j, n = 100;
for( i=0; i < n; i++ )
{
    for( j=0; j < n; j++ )
    {
        printf(“I love FishC.com\n”);
    }
}

也就是说外层循环每执行一次,内层循环就执行100次,那总共程序想要从这两个循环出来,需要执行100*100次,也就是n的平方。所以这段代码的时间复杂度为O(n^2)。

总结:如果有三个这样的嵌套循环就是n^3。所以总结得出,循环的时间复杂度等于循环体的复杂度乘以该循环运行的次数。



int i, j, n = 100;
for( i=0; i < n; i++ )
{
    for( j=i; j < n; j++ )
    {
        printf(“I love FishC.com\n”);
    }
}

由于当i=0时,内循环执行了n次,当i=1时,内循环则执行n-1次……当i=n-1时,内循环执行1次,所以总的执行次数应该是:
n+(n-1)+(n-2)+…+1 = n(n+1)/2
n(n+1)/2 = n^2/2+n/2
用我们推导大O的攻略,第一条忽略,因为没有常数相加。第二条只保留最高项,所以n/2这项去掉。第三条,去除与最高项相乘的常数,最终得O(n^2)。

(4)对数阶


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

由于每次i*2之后,就距离n更近一步,假设有x个2相乘后大于或等于n,则会退出循环。
于是由2^x = n得到x = log(2)n,所以这个循环的时间复杂度为O(logn)。

2.3函数调用的时间复杂度分析

nt i, j;
for(i=0; i < n; i++) {
    function(i);
}
void function(int count) {
    printf(%d”, count);
}

函数体是打印这个参数,这很好理解。function函数的时间复杂度是O(1),所以整体的时间复杂度就是循环的次数O(n)。

假如function是下面这样:

void function(int count) {
    int j;
    for(j=count; j < n; j++) {
        printf(%d”, j);
    }
}

事实上,这和之前平方阶的时候举的第二个例子一样:function内部的循环次数随count的增加(接近n)而减少,所以根据游戏攻略算法的时间复杂度为O(n^2)。

n++; //1
function(n); //1
for(i=0; i < n; i++) { //n
    function(i);
}
for(i=0; i < n; i++) { //n^2
    for(j=i; j < n; j++) {
        printf(%d”, j);
    }
}
void function(int count) {
    printf(%d”, count);
}

为:1+1+n+n2,所以最后是O(n2)

常用的时间复杂度所耗费的时间从小到大依次是:
O(1) < O(logn) < (n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
如图:
在这里插入图片描述

1.4 最坏情况与平均情况

我们查找一个有n个随机数字数组中的某个数字,最好的情况是第一个数字就是,那么算法的时间复杂度为O(1),但也有可能这个数字就在最后一个位置,那么时间复杂度为O(n)。
平均运行时间是期望的运行时间。
最坏运行时间是一种保证。在应用中,这是一种最重要的需求,通常除非特别指定,我们提到的运行时间都是最坏情况的运行时间。

3.空间复杂度

3.1空间复杂度计算方法

(1) 忽略常数,用O(1)表示
(2)递归算法的空间复杂度=递归深度N*每次递归所要的辅助空间
(3)对于单线程来说,递归有运行时堆栈,求的是递归最深的那一次压栈所耗费的空间的个数,因为递归最深的那一次所耗费的空间足以容纳它所有递归过程。

a = 0
b = 0
print(a,b

空间复杂度O(n)=O(1);


def fun(n):
    k = 10
    if n == k:
        return n
    else:
        return fun(++n)

递归实现,调用fun函数,每次都创建1个变量k。调用n次,空间复杂度O(n*1)=O(n)。

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

变量的内存分配发生在定义的时候,因为temp的定义是循环里边,所以是n*O(1)


temp=0;
for(i=0;i<n;i++):
  temp = i
temp定义在循环外边,所以是1*O(1) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值