复杂度分析

事后统计法:运行代码,通过统计,监控,得到算法执行的时间和占用的内存。

事后统计法的局限性:

1、测试结果依赖于测试环境。

  • I9 cpu 和I3 cpu运算速度的不同,导致代码执行的快慢也不同。

2、测试结果受到数据规模的影响很大。

  • 对于同一个排序算法,待排序数据的有序度不一样,对于执行时间就有很大的区别;测试数据的规模大小,也会影响算法的性能,小规模的数据培训,插入可能反而比快速排序要快。

大O复杂度表示法:从cpu角度来看,每行代码都执行类似的操作:读数据-运算-写数据,尽管每行代码对应cpu执行个数,时间都不一样,但是做粗略运算,每行代码时间近似看做相等,计作unit_time。

例子:

int cal(int n )

{

     int sum = 0;   //unit_time

      int i = 1;    //unit_time

      int j = 1;     //一个unit_time

     for(;i<=n;i++)     //n个unit_time

        {

             j =1 ;             //n个unit_time

                 for(;j<=n;j++)          //n个 n个unit_time

                     {

                         sum = sum + i * j ;      // n个  n个unit_time

                     }

        }

}

//总的时间复杂度  T(n) =  (2n² + 2n + 3) * unit_time。类似于高数求极限 所以T(n) = O(n²)。

时间复杂度分析的三个使用的方法:

1、只关心循环次数最多的一段代码 //类似于高数求极限 当n->∞,求T(n)

2、加法法则:总时间复杂度等于量级最大的那段复杂度。

代码分为三个部分,sum_1 ,sum_2,sum_3,第一段代码虽然执行了100次,但是仍然是常量执行时间,与n的规模无关。第二段第三段为O(n)和O(n²)  //还是求高数中极限的问题 。当n->∞,lim T(n)  = O(n²)

3、乘法法则:嵌套代码的复杂度等于内外代码复杂度的乘积。

几种常见时间复杂度分析:

对于复杂度量级可以粗略分为两类:多项式量级和非多项式量级。非多项式用绿色波浪标注。把时间复杂度为非多项式量级的算法称作NP(Non-Deterministic polynomial)非确定多项式问题。当数据规模 n 越来越大时,非多项式量级算法的执行时间会急剧增加,求解时间会无限增长。

常见的多项式量级:

1、O(1):代码执行时间不随着n的增大而增大,都称作常数量级的时间复杂度。

2、O(logn)、O(nlogn)

int i = 1

while(i <= n)

{

i = i *2 ;

}

变量i的值从1开始取,没循环一次乘以2,取值过程为一个等比数列:

当第x次,恰好大于n,所以x = log2n,时间复杂度O(log2n)

但是实际上,不管是以几为底( i = i * 3 ;//以三为底, i = i * 10 ;//以十为底),都可以把所有对数阶时间复杂度记作O(logn)

因为对数之间是可以相互转换的,log3n等于log3 2 * log 2 n 所以相差一个常量 C = log 3 2 ,可以忽略。所以在对数阶时间复杂度表达里面,忽略对数的“底”,统一记作O(log n),所谓O(nlogn) 就是把时间复杂度O(logn)的代码执行n次,如归并排序和快速排序。

3、O(m+n) 、 O(m * n)

int cal(int m, int n) {
  int sum_1 = 0;
  int i = 1;
  for (; i < m; ++i) {
    sum_1 = sum_1 + i;
  }

  int sum_2 = 0;
  int j = 1;
  for (; j < n; ++j) {
    sum_2 = sum_2 + j;
  }

  return sum_1 + sum_2;
}
m和n是两个数据规模,无法事先评估m和n的大小,所以上述代码时间复杂度O(m + n)。同理得到O(m * n )

时间复杂度全称:渐进时间复杂度,表示算法的执行时间和数据规模之间的增长关系。

空间复杂度全称:渐进空间复杂度,表示算法的存储空间与数据规模之间的增长关系。

最好时间复杂度(best case time complexity)

最坏情况时间复杂度(worst case time complexity)

平均情况时间复杂度(average case time complexity)

均摊时间复杂度(amortized time complexity)

最好时间复杂度(best case time complexity)

最坏情况时间复杂度(worst case time complexity)

//查找n个数数组中的某一个数

// n 表示数组 array 的长度
int find(int[] array, int n, int x) {
  int i = 0;
  int pos = -1;
  for (; i < n; ++i) {
    if (array[i] == x) {
       pos = i;
       break;
    }
  }
  return pos;
}
//最好时间复杂度 查找第一个元素即为我们需要的变量x --------O(1)  

//最坏时间复杂度 遍历整个数组找到我们需要的变量x     -------O(n)

平均情况时间复杂度(average case time complexity)

查找变量x在数组中的位置,有n+1种情况:在数组的0~n-1位置中不在数组中,考虑每种情况,然后再除以情况n + 1种情况数。

根据时间复杂度的大O标记法,省去系数,常量,低阶,简化后为:O(n)。

但是实际情况并不是这样,数组的0~n-1位置中不在数组中发生的概率是不同的。假设在数组中和不在数组中的概率1/2,另外查找数据出现在数组的0~n-1位置中任意位置的概率就是1/(2n),所以平均时间复杂度:

该值叫做概率论中加权平均值,也叫期望值,所以平均时间复杂度全称:加权平均时间复杂度或者期望时间复杂度。

根据时间复杂度的大O标记法,省去系数,常量,低阶,简化后为:O(n)。

均摊时间复杂度:对应的分析方法,摊还分析(平摊分析)

平均时间复杂度只在某些特殊情况会用到,而均摊时间复杂度的应用场景更加特殊和有限。

数组的插入:如果数组满了,则遍历数组,把所有累加和存放到第一个位置;否则直接插入。时间复杂度O(n)和O(1)。每一次O(n)的操作后面都会跟着n-1次O(1)的操作,所以把耗时多的那次操作均摊到接下来n-1次耗时少的操作,均摊下来,这一组连续操作的均摊时间复杂度就是O(1)。

然而什么时候使用均摊时间复杂度呢?

-->对于一个数据结构进行一组连续操作中,大部分情况下时间复杂度都很低,只有个别情况下时间复杂度比较高,而且这些操作之间满足前后连续的时序关系,这样就可以把这一组操作放在一起分析,看是否能把较高的时间复杂度的耗时,平摊到其他时间复杂度比较低的操作上,并且能够使用均摊时间复杂度分析的场合,一般均摊时间复杂度等于最好情况时间复杂度。

猜你喜欢

转载自blog.csdn.net/qq_34269988/article/details/82944088