【入门篇】2 # 复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度

简介: 【入门篇】2 # 复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度

说明

【数据结构与算法之美】专栏学习笔记。



为什么引入这些时间复杂度

先看下面代码

//  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;
}

上面代码中如果没有 break;那么代码的时间复杂度就是 O(n),但是代码的循环中存在提前退出循环的操作,普通的时间复杂度分析解决不了这个问题。为了表示代码在不同情况下的不同时间复杂度,就引入了最好、最坏、平均、均摊时间复杂度。



最好、最坏情况时间复杂度

  • 最好情况时间复杂度:在最理想的情况下,执行代码的时间复杂度。
  • 最坏情况时间复杂度:在最糟糕的情况下,执行代码的时间复杂度。


上面代码在最理想的情况下,查找的变量 x 正好是数组的第一个元素,时间复杂度就是 O(1);在最糟糕的情况下,就是把整个数组都遍历一遍,时间复杂度就成了 O(n)。




平均情况时间复杂度


最好、最坏比较极端,为了更好地表示平均情况下的复杂度,引入了平均情况时间复杂度。


平均时间复杂度:又叫加权平均时间复杂度(或者期望时间复杂度),用代码在所有情况下执行的次数的加权平均值表示。


上面的代码中查找的变量 x 在数组中的位置,有 n+1 种情况:


   在数组的 0~n-1 位置中:n 种

   不在数组中:1 种


假设在数组中与不在数组中的概率都为 1/2,那么出现在 0~n-1 中任意位置的概率就是 1/(2n)。

52554ffbd9f546ce8a747956eb83738a.png

根据下面等差数列的公式:

   1+2+3+…+n = n(1+n)/2


可以快速计算出上面计算式等于(3n + 1)/4,推出平均时间复杂度为 O(n)。


大多数情况下,不需要区分最好、最坏、平均情况时间复杂度三种情况。只有同一块代码在不同的情况下,时间复杂度有量级的差距,才会使用这三种复杂度表示法来区分。



均摊时间复杂度


下面看一个往数组中插入数据的例子:当数组满了之后,也就是代码中的 count == array.length 时,用 for 循环遍历数组求和,并清空数组,将求和之后的 sum 值放到数组的第一个位置,然后再将新的数据插入。但如果数组一开始就有空闲空间,则直接将数据插入数组。这里均摊的话不止一次调用 insert 的,可以理解为有外循环。

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;
}


最理想的情况下,数组中有空闲空间,时间复杂度为 O(1);最坏的情况下,数组中没有空闲空间,需要遍历一遍,其时间复杂度为 O(n)。


根据数据插入的位置的不同,可以分为 n 种情况,每种情况的时间复杂度是 O(1)。另外一种在数组没有空闲空间时插入一个数据,时间复杂度是 O(n)。这样就有 n + 1 种情况:得到平均时间复杂度为 O(1)。


8602f23fbe8c4f71b09d9a5bb5866923.png


这里并不需要像之前的平均复杂度分析方法那样,找出所有的输入情况及相应的发生概率,然后再计算加权平均值。针对这种特殊的场景,就引入了一种更加简单的分析方法均摊时间复杂度,又叫摊还分析(或者叫平摊分析)。


均摊分析的大致思路:这里对于 insert() 函数来说,O(1) 时间复杂度的插入和 O(n) 时间复杂度的插入,出现的频率是非常有规律的,数组已经满了,也就是 O(n) 是无空闲的状态,每满一次就会清空数组,清空数组后重新开始写 n - 1 次才会进行下一次清空,每次写入的复杂度就是O(1),有 O(n) 后接着 n - 1 个 O(1),循环往复。所以把耗时多的那次操作均摊到接下来的 n-1 次耗时少的操作上,均摊下来,这一组连续的操作的均摊时间复杂度就是 O(1),可以理解为 (n + n - 1)/n。


均摊时间复杂度就是一种特殊的平均时间复杂度,能够应用均摊时间复杂度分析的场合,一般均摊时间复杂度就等于最好情况时间复杂度。



目录
相关文章
|
7月前
|
存储 弹性计算 安全
阿里云服务器租用价格参考:最新包年包月收费标准与活动价格整理
购买阿里云服务器一年多少钱?2025年阿里云服务器价格又调整了,轻量云服务器2核2G200M峰值带宽38元一年、2核4G4M带宽60GB ESSD云盘298元一年,e实例云服务器2核2G3M带宽99元1年,u1实例2核4G5M带宽199元一年、4核8G云服务器955元一年,4核16G10M云服务器70元1个月、210元3个月,8核32G10M带宽160元1个月、480元3个月。对于想要上云的用户来说,了解阿里云服务器的价格体系是选择合适产品的第一步。那么,2025年购买阿里云服务器到底需要多少钱呢?本文为大家整理汇总了截止目前阿里云服务器的最新包年包月收费标准和活动价格情况,以供参考。
|
12月前
|
缓存 数据挖掘 测试技术
目标检测实战(三):YOLO-Nano训练、测试、验证详细步骤
本文介绍了YOLO-Nano在目标检测中的训练、测试及验证步骤。YOLO-Nano是一个轻量级目标检测模型,使用ShuffleNet-v2作为主干网络,结合FPN+PAN特征金字塔和NanoDet的检测头。文章详细说明了训练前的准备、源代码下载、数据集准备、参数调整、模型测试、FPS测试、VOC-map测试、模型训练、模型测试和验证等步骤,旨在帮助开发者高效实现目标检测任务。
599 0
目标检测实战(三):YOLO-Nano训练、测试、验证详细步骤
|
机器学习/深度学习 存储 算法
算法时间复杂度分析
这篇文章讲解了如何分析算法的时间复杂度,包括关注循环执行次数最多的代码段、总复杂度的确定、嵌套代码复杂度的计算方法,并提供了大O阶的推导步骤和常见时间复杂度的列表,同时还介绍了空间复杂度的概念及其重要性。
|
搜索推荐
九大排序算法时间复杂度、空间复杂度、稳定性
九大排序算法的时间复杂度、空间复杂度和稳定性,提供了对各种排序方法效率和特性的比较分析。
735 1
|
机器学习/深度学习 自然语言处理 算法
大模型Prompt-Tuning技术入门(一)
Prompt-Tuning是NLP领域的新兴技术,旨在减少预训练模型Fine-Tuning的需要。它通过构造提示(Prompt)使预训练模型能适应各种任务,降低了语义偏差和过拟合风险。Prompt作为任务的“提示词”,可以是人工定义、自动搜索或生成的模板,与预训练的MLM头结合使用,只需少量甚至无标注数据,通过标签词映射进行预测。此方法从GPT-3的In-Context Learning发展至今,包括了连续Prompt、大规模模型的Instruction-tuning和Chain-of-Thought等进展。 Prompt-Tuning是向少监督、无监督学习迈进的关键研究。
|
机器学习/深度学习 算法
【机器学习】P问题、NP问题、NP-hard、NP-C问题解析与举例理解
本文解析了P问题、NP问题、NP-hard问题以及NP-Complete问题的概念,并通过实例帮助理解NP问题的特点和复杂性。
3430 1
|
存储
好看又规范的Github Readme 制作指南
本文是关于制作规范且外观吸引人的GitHub README文件的指南,包括了README的基本结构、美化技巧,以及如何使用Markdown格式、徽标和图片来增强文档的可读性和吸引力。
1127 0
|
JavaScript 中间件 开发者
深入浅出Node.js中间件机制
【8月更文挑战第31天】本文将带你领略Node.js中间件的奥秘,通过直观的案例分析,揭示其背后的设计哲学。你将学会如何运用中间件构建强大而灵活的后端应用,以及在面对复杂业务逻辑时如何保持代码的清晰与高效。
byte-buddy实现mybatis-plus动态mapper
byte-buddy实现mybatis-plus动态mapper
357 0
|
JavaScript Java 测试技术
基于SpringBoot+Vue的高校门诊管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue的高校门诊管理系统的详细设计和实现(源码+lw+部署文档+讲解等)
84 0