数据结构与算法(二) - 复杂度分析

数据结构与算法(二)-复杂度分析

为什么要进行复杂度分析?

  • 和性能测试相比,复杂度分析有不依赖于环境,成本低,效率高,易操作,指导性强的特点.
  • 掌握复杂度分析,将能编写出性能更优的代码,有利于降低系统开发和维护成本.

1.时间复杂度

1.1 大O表示法

假如有一段代码:

public void print(int n){
    int a = 1; //执行1次
    for(int i=0;i<n;++i){//执行n次
        System.out.println(a+i);//执行n次
    }
}

假设我们执行一行代码平均需要用时是一个常数 t = time,那计算上面代码(算法)的用时,就是执行总行数乘以time。上面的代码总时间就是:

T(n) = (1+2n)*time

其中n是方法参数,代表算法的数据规模。可以看出,执行用时和(1+2n)成正比。我们用大O表示法表示该算法的时间复杂度就是:O(1+2n)

大O表示法会忽略常量、低阶和系数,所以记作:O(n)

大O表示法描述的是随着数据规模n增长时,算法的增长变化趋势。并不代表实际的执行时间。如果算法的执行时间和数据规模n无关,则是常量阶,计作 :O(1)

一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是Ο(1)

1.2 复杂度分析方法

如何计算程序(算法)时间复杂度的问题

1.代码循环次数最多原则

在分析一个算法或者一个代码的时间复杂度时,只需要关注循环次数最多的那一段代码即可.比如O(2*n*n*n + n*n)的时间复杂为O(n*n*n).下面看代码示例:

public void print(int n){
    for(int i=0;i<10;++i) System.out.println(i);//循环10次
    int i = 1;//一次
    while(n>=i) i = i*2;//见下分析
}

在这里插入图片描述

2.加法原则

总时间复杂度等于量级最大的那段代码的时间复杂度.比如:

public void print(int n){
    //执行100次 属于常量级 忽略
    for(int i=0;i<100;++i){
        System.out.println(i);
    }
    
    //执行n次 时间复杂度O(n)
    for(int i=0;i<n;++i){
        System.out.println(i);
    }
      
    //嵌套循环 执行n*n次 时间复杂度O(n*n)
    for(int i=0;i<n;++i){
        for(int j=0;j<n;++j){
            System.out.println(i+j);
        }
    }
    //嵌套循环 执行n*n+n次 时间复杂度O(n*n)
    for(int i=0;i<n;++i){
        for(int j=0;j<n;++j){
            System.out.println(i+j);
        }
        System.out.println(i);
    }
}
//T(n) = O(n) + O(n*n) = O(n*n+n) = O(n*n)
3.乘法原则

嵌套代码的复杂度等于嵌套内外代码复杂度的乘积,如:

//嵌套循环 执行n*n次 时间复杂度O(n*n)
for(int i=0;i<n;++i){
    for(int j=0;j<n;++j){
        System.out.println(i+j);
    }
}

1.3 常见的时间复杂度

常见的时间复杂度和其数学特性:

大O表示法复杂度量级
O(1)常量阶
O(logn)对数阶
O(n)线性阶
O(nlogn)线性对数阶
O(n2)、O(n3)、…O(n^k)平方阶、立方阶、…k次方阶
O(2^n)指数阶
O(n!)阶乘阶

通过曲线的变化,能够直观了解几种时间复杂度的情况。

在这里插入图片描述

其中最后两个,指数和阶乘是非多项式量级,其余都是多项式量级。我们把时间复杂度是非多项式量级的算法问题称为NP(Non-Deterministic-Polynomial,非确定多项式)问题。简言之,就是随着数据规模增长,时间复杂度失控,会非常复杂耗时很久的一类问题,有关NP更详细的细节请参考《算法导论》中的章节。有关 “失控”,看下文的拟合曲线直观感受下。

为了更加直观我们取一组样本数据,来拟合一些曲线,更准确直观的感受一下。

首先对比 n的6次方阶底数为2的指数阶 随数据规模n的变化曲线:

在这里插入图片描述

在看一卡指数和阶乘对比:

在这里插入图片描述

上面我们已经感受了指数量级的恐怖,但是阶乘更加凶残,和阶乘相比,指数让你觉得有一种常量的错觉。具体分析我就不再计算了。

1.4 最好、最坏和平均时间复杂度分析

我利用下面一段代码来进行分析:

//n是数组nums的长度
public int find(int[]nums,int n,int target){
    for(int i=0;i<n;++i){
        if(nums[i]==target){
            return i;
        }
    }
    return -1;
}
  • 最好情况。最理想的情况下的复杂度。最理想的情况就是第一个就是要找的,所以最好情况下时间复杂度是O(1)

  • 最坏情况。最不理想的情况下的复杂度。就是要找的数在最后一个,所以需要遍历n次,最坏情况下时间复杂度是O(n)

  • 平均时间复杂度

    分析:为了方便说明,我们假设数组中一定存在要找的数。而要找的数在0 - n-1这些位置出现的概率相同,都是 1/n ,所以考虑每种情况下总共要查找的次数,求出总数,然后再除以 可能的情况数,就是平均要查找的次数,去除常量和系数就是O(n):

在这里插入图片描述

  • 均摊分析法

    平时不太常用,它指的是算法大部分时间是一种复杂度,偶尔会出现复杂度加剧,可以通过将这些偶发情况均摊到出去,计算均摊后的时间复杂度。一般经验告诉我们,均摊后还是一般情况下的复杂度。就好像被“稀释”了。比如栈在动态扩容时,入栈操作时间复杂度由O(1)退化到O(n), 但是扩容是在内存到临界点时才触发一次,所以总的时间复杂度还是O(1).


2.空间复杂度

空间复杂度全程是渐进空间复杂度,表示算法占用的存储空间与数据规模之间的增长关系.下面用一段代码说明一下:

public void print(int n){
    int i=0;
    int[] a = new int[];
    for(int i=0;i<n;i++){
		a[i] = i * i;
    }
    for(int i=0;i<n;i++){
		System.out.println(a[i]);
    }
}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值