美文网首页
算法的复杂度分析

算法的复杂度分析

作者: 天命_风流 | 来源:发表于2020-03-12 21:15 被阅读0次

如果想要分析一个程序的性能,你可以尝试将程序运行,然后收集它的运行数据,就可以得到程序运行所需要的时间和内存。我们将这种分析方法称为事后统计法。
事后统计法是非常朴素的分析方法,但是也两点问题:

  1. 程序的运行需要依赖硬件环境,比如同一个程序在两台性能不一的电脑上的表现是不同的。
  2. 程序运行时间往往收到数据规模的影响,例如在排序算法中,快排是最优秀的算法,如果只使用几个数据进行排序,可能它的表现还不如冒泡排序,但是当使用上万个数据进行排序的时候,他们的性能差异就会显现。(这种差异可能不是十倍,而是百倍千倍)

由于这些问题,我们一般使用复杂度分析的方法分析一个算法的性能,这就是大O复杂度表示法

大O复杂度表示法:

大O时间复杂度表示法:

抛去计算机底层的影响,我们可以使用大O时间复杂度表示法来表示代码执行时间随数据规模增长的变化趋势,简称时间复杂度
分析时间复杂度有三个比较使用的方法:

1.只关注循环执行次数最多的一段代码:

敲黑板:循环 和 最多
下面有一段代码:

int cal(int n) {
   int sum = 0;
   int i = 1;
   for (; i <= n; ++i) {
     sum = sum + i;
   }
   return sum;
 }

上面的代码中,2、3行代码都是常量级的执行时间,和n无关,在复杂度分析中可以忽略。循环执行次数最多代码是 for 循环中的代码,它被执行了 n 次,所以总的时间复杂度是O(n)

2.加法法则:总复杂度等于量级最大的那段代码的复杂度:

看下面的代码:

int cal(int n) {
   int sum_1 = 0;
   int p = 1;
   for (; p < 100; ++p) {
     sum_1 = sum_1 + p;
   }

   int sum_2 = 0;
   int q = 1;
   for (; q < n; ++q) {
     sum_2 = sum_2 + q;
   }
 
   int sum_3 = 0;
   int i = 1;
   int j = 1;
   for (; i <= n; ++i) {
     j = 1; 
     for (; j <= n; ++j) {
       sum_3 = sum_3 +  i * j;
     }
   }
 
   return sum_1 + sum_2 + sum_3;
 }

上面的代码中,sum_2部分的代码的复杂度为O(n),sum_3代码的复杂度为O(n2),根据加法法则,cal函数的复杂度为O(n2)

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

看下面的代码:


int cal(int n) {
   int ret = 0; 
   int i = 1;
   for (; i < n; ++i) {
     ret = ret + f(i);
   } 
 } 
 
 int f(int n) {
  int sum = 0;
  int i = 1;
  for (; i < n; ++i) {
    sum = sum + i;
  } 
  return sum;
 }

可以看到,在 cal 的 for 循环中嵌套了 f 函数。第一部分的 for 循环贡献了 n 的复杂度,而 f 函数也贡献了 n 的复杂度,根据乘法法则,cal的复杂度为 O(n2)。

大O空间复杂度表示法:

空间复杂度的定义和时间复杂度类似:表示算法需要的存储空间与数据规模之间的增长关系。
你只需要分析在算法中申请的存储空间即可,常见的空间复杂度有:O(1),O(n),O(n2)

最好、最坏、平均、均摊 时间复杂度

同一个算法在给定不同数据的情况下性能可能会出现很大的差异,例如:如果一个数组已经基本有序,那么使用快速排序则有O(n2)的时间复杂度,而最好的情况下复杂度为O(nlogn)。
基于上面的情况,我们引入了各种复杂度的度量:

  • 最好情况时间复杂度:在最理想的情况下,执行这段代码的时间复杂度
  • 最坏情况时间复杂度:在最糟糕的情况下,执行这段代码的时间复杂度
  • 平均时间复杂度:根据概率论的理论,平均时间复杂度的值为代码执行的加权平均值,也就是它的期望值。所以你也可以将之称为加权平均时间复杂度。
  • 均摊时间复杂度:你可以将其理解为一种特殊的平均时间复杂度:
    先来看下面一段代码:
 // array表示一个长度为n的数组
 // 代码中的array.length就等于n
 int[] array = new int[n];
 int count = 0;
 
 void insert(int val) {
    if (count == array.length) {
       int sum = 0;
       for (int i = 0; i < array.length; ++i) {
          sum = sum + array[i];
       }
       array[0] = sum;
       count = 1;
    }

    array[count] = val;
    ++count;
 }

你会发现,如果我们一直使用insert函数向数组array中添加数据,在没有到达n,只需要O(1)的时间复杂度,但是如果放入第n个数据,复杂度就会变为O(n),这就出现了在执行过程中操作的复杂度不同的情况,针对这种情况,我摘取了下面一段话:

对一个数据结构进行一组连续操作中,大部分情况下时间复杂度都很低,只有个别情况下时间复杂度比较高,而且这些操作之间存在前后连贯的时序关系,这个时候,我们就可以将这一组操作放在一块儿分析,看是否能将较高时间复杂度那次操作的耗时,平摊到其他那些时间复杂度比较低的操作上。而且,在能够应用均摊时间复杂度分析的场合,一般均摊时间复杂度就等于最好情况时间复杂度。

注意:只有在最好、最坏、平均时间复杂度在量级上存在差异的时候,区分它们才有意义。

以上就是对算法的复杂度分析的全部内容。

相关文章

  • 数据结构与算法学习-复杂度分析

    前言 这一篇笔记主要记录总结了什么是算法复杂度?、为什要做算法复杂度分析?、如何做算法复杂度分析?、常用的复杂度级...

  • 算法

    重拾算法:算法效率分析(一)(空间复杂度和时间复杂度) 详解算法的各种复杂度的差别有多大(带图) 算法复杂度 选择...

  • 数据结构与算法-复杂度分析

    时间、空间复杂度:衡量算法执行小路的指标,数据结构与算法离不开时间、空间复杂度分析,复杂度分析是算法的精髓。 为什...

  • 算法复杂度分析与最大子串问题

    算法复杂度分析 算法复杂度基本定义 算法复杂度分析基于以下四条定义: 如果存在常数c与$n_{0}$使$N \ge...

  • 算法复杂度

    算法复杂度 算法复杂度的目的:分析代码执行的时间成本。我们从五个方面来介绍算法复杂度:时间复杂度、时间复杂度分类、...

  • 数据结构-复杂度分析

    为什么需要复杂度分析? 复杂度分析实在太重要了。复杂度分析是整个算法学习的精髓,只要掌握了它,数据结构和算法的内容...

  • map:169.求众数(投票算法)

    求众数 哈希Map 复杂度分析 时间复杂度:O(N) 空间复杂度: O(N) 投票算法 复杂度分析

  • 时间复杂度和空间复杂度笔记

    复杂度分析笔记 复杂度主要分为时间和空间复杂度 时间复杂度:算法(程序)执行的时间变化趋势 空间复杂度:算法(程序...

  • 【3】时间复杂度

    算法时间复杂度 算法时间复杂度的定义:在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T...

  • 算法复杂度分析

    复杂度分析: 数据结构和算法解决的两大问题:快和省。运行快,储存省。 复杂度分析是算法学习的精髓:时间复杂度,空间...

网友评论

      本文标题:算法的复杂度分析

      本文链接:https://www.haomeiwen.com/subject/jybbjhtx.html