复杂度分析(下)

复杂度分析(下)

继续上篇,这篇将介绍四个复杂度分析方面的知识点:最好情况时间复杂度、最坏情况时间复杂度、平均情况时间复杂度、均摊时间复杂度。

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



我们以数组查找举例,遍历数组找指定元素,找到则立即返回该元素所在数组的下标位置,没找到则返回-1。代码比较简单我就不写出来了。

我们来分析一下,

最好情况时间复杂度:最好情况当然是数组的第一个元素就是查找的元素,为O(1)

最坏情况时间复杂度:最坏情况就是数组的最后一个元素为查找的元素,需要遍历完整个数组才能找到该元素,或者数组中不存在该元素,那么也需要遍历完数组,为O(n)

总结:

最好情况时间复杂度,就是在最理想的情况下,执行这段代码的时间复杂度。

最坏情况时间复杂度,就是在最糟糕的情况下,执行这段代码的时间复杂度。

2.平均情况时间复杂度



最好和最坏情况时间复杂度都是对应极端情况下的代码复杂度,发生的概率并不大。为了更好地表示平均情况下的复杂度,我们需要引入另一个概念:平均情况时间复杂度。

还是刚刚那个例子,看看怎么分析平均情况时间复杂度。

要查找的变量x在数组中的位置,有n+1种情况:在数组的0~n-1的位置上,和不在数组中。另外我们假设x在或不在数组中的概率各为1/2,且x出现在数组各个位置的概率也是一样的,为1/n。

根据概率乘法法则,要查找的元素出现在0~n-1的任意位置的概率就是1/2n。

那平均时间复杂度的计算过程如下:

这个值就是概率论中的加权平均值,也叫作期望值,所以平均时间复杂度的全称应该叫加权平均时间复杂度或者期望时间复杂度

用大O表示法来表示,去掉系数和常量,这段代码的加权平均时间复杂度是O(n)。

3.均摊时间复杂度



均摊时间复杂度只在某些特殊情况下才会使用。

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

这段代码实现了一个往数组中插入数据的功能。当数组满了之后,也就是代码中的 count == array.length 时,我们用 for 循环遍历数组求和,并清空数组,将求和之后的 sum 值放到数组的第一个位置,然后再将新的数据插入。但如果数组一开始就有空闲空间,则直接将数据插入数组。

我们先用上面介绍了三种方法分析一下:

最理想的情况下,数组中有空闲空间,我们只需要将数据插入到数组下标为 count 的位置就可以了,所以最好情况时间复杂度为 O(1)。

最坏的情况下,数组中没有空闲空间了,我们需要先做一次数组的遍历求和,然后再将数据插入,所以最坏情况时间复杂度为 O(n)。

那平均时间复杂度是多少呢?答案是 O(1)。我们还是可以通过前面讲的概率论的方法来分析。假设数组的长度是 n,根据数据插入的位置的不同,我们可以分为 n 种情况,每种情况的时间复杂度是 O(1)。除此之外,还有一种“额外”的情况,就是在数组没有空闲空间时插入一个数据,这个时候的时间复杂度是 O(n)。而且,这 n+1 种情况发生的概率一样,都是 1/(n+1)。所以,根据加权平均的计算方法,我们求得的平均时间复杂度就是:

但其实这里平均复杂度没必要这么复杂。我们来分析一下代码。

insert()方法在大部分情况下,时间复杂度都为O(1),只有在个别情况下,复杂度才有O(n)。且O(1)和O(n)的出现是有规律的,一个O(n)插入之后,紧跟着n-1个O(1)插入,循环往复。

所以,针对这种特殊场景的复杂度分析,我们并不需要向之前那样通过求概率的方法计算加权平均值。

引入一种更加简单的分析方法叫做摊还分析法,通过摊还分析得到的时间复杂度叫做均摊时间复杂度。

每一次 O(n) 的插入操作,都会跟着 n-1 次 O(1) 的插入操作,所以把耗时多的那次操作均摊到接下来的 n-1 次耗时少的操作上,均摊下来,这一组连续的操作的均摊时间复杂度就是 O(1)。这就是均摊分析的大致思路。

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

 均摊时间复杂度就是一种特殊的平均时间复杂度

总结



这篇文章接着上一篇介绍了几个新的复杂度分析相关的概念,分别有:最好情况时间复杂度,最坏情况时间复杂度,平均情况时间复杂度,均摊时间复杂度。之所以引入这几个复杂度概念,是因为同一段代码,在不同输入的情况下,复杂度的量级有可能是不一样的。

原文地址:https://www.cnblogs.com/tczw/p/12117172.html

时间: 2024-08-15 01:21:38

复杂度分析(下)的相关文章

Chapter4 复杂度分析(下):浅析最好,最坏,平均,均摊时间复杂度

四个复杂度分析: 1:最好情况时间复杂度(best case time complexity) 2:最坏情况时间复杂度(worst case time complexity) 3:平均情况时间复杂度(average case time complexity) 4:均摊时间复杂度(amortized time complexity) for (; i < n; ++i) { if (array[i] == x) { pos = i; break; } } 分析:1:最好情况时间复杂度:O(1) 2

复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度

时间复杂度分析有哪些? 最好情况时间复杂度(best case time complexity) 最坏情况时间复杂度(worst case time complexity) 平均情况时间复杂度(average case time complexity) 均摊时间复杂度(amortized time complexity) 最好.最坏情况时间复杂度 最好情况时间复杂度就是在最理想的情况下,执行这段代码的时间复杂度. 最好情况时间复杂度就是在最糟糕的情况下,执行这段代码的时间复杂度. 来看看下面这段

算法录 之 复杂度分析。

一个算法的复杂度可以说也就是一个算法的效率,一般来说分为时间复杂度和空间复杂度... 注意接下来说的均是比较YY的,适用与ACM等不需严格分析只需要大致范围的地方,至于严格的算法复杂度分析的那些数学证明,主定理什么的在<算法导论>这本书上有十分详细的讲解,网上应该也会有人写过,这里就不多说了(其实,是我不会而已o(╯□╰)o...). — 到底啥是复杂度呢?先来个栗子. 小明有10个苹果,有一天他饿了,然后准备吃掉一个苹果,但是小明有中二病,他要吃里面重量最大的那个,于是...他需要一个找到那

Python 文本相似度分析

环境 Anaconda3 Python 3.6, Window 64bit 目的 利用 jieba 进行分词,关键词提取 利用gensim下面的corpora,models,similarities 进行语料库建立,模型tfidf算法,稀疏矩阵相似度分析 代码 # -*- coding: utf-8 -*- import jieba from gensim import corpora, models, similarities from collections import defaultdi

斐波那契数与二分法的递归与非递归算法及其复杂度分析

1. 什么是斐波那契数? 这里我借用百度百科上的解释:斐波那契数,亦称之为斐波那契数列(意大利语: Successione di Fibonacci),又称黄金分割数列.费波那西数列.费波拿契数.费氏数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.--在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字来说,就是斐波那契数列列由 0 和 1 开始,之后的斐波那契数列系数就由之前的两数相加.特别指出:0不是第一

杨辉三角(Pascal Triangle)的几种C语言实现及其复杂度分析

说明 本文给出杨辉三角的几种C语言实现,并简要分析典型方法的复杂度. 本文假定读者具备二项式定理.排列组合.求和等方面的数学知识. 一  基本概念 杨辉三角,又称贾宪三角.帕斯卡三角,是二项式系数在三角形中的一种几何排列.此处引用维基百科上的一张动态图以直观说明(原文链接http://zh.wikipedia.org/wiki/杨辉三角): 从上图可看出杨辉三角的几个显著特征: 1. 每行数值左右对称,且均为正整数. 2. 行数递增时,列数亦递增. 3. 除斜边上的1外,其余数值均等于其肩部两数

算法9-4:最大流算法复杂度分析

前面一节介绍了Ford-Fulkerson算法.那么这个算法是否一定能够在有限步骤内结束?要多少步骤呢? 这个问题的答案是,该算法确实能够在有限步骤之内结束,但是至于需要多少步骤,就要仔细分析. 为了分析问题,需要假定图中所有边的容量都是整数.但是有个严重的问题,比如下图中,如果使用Ford-Fulkerson算法,需要迭代200次才能结束. 首先将所有边的容量都初始化为0. 第一次迭代和第二次迭代之后,两条边各增加了1. 到最后200次迭代之后整个算法才结束. 这还不算最坏的情况.因为整数最多

比较排序算法及复杂度分析

比较排序算法分类 比较排序(Comparison Sort)通过对数组中的元素进行比较来实现排序. 比较排序算法(Comparison Sorts) Category Name Best Average Worst Memory Stability  插入排序  (Insertion Sorts) 插入排序 (Insertion Sort) n n2 n2 1 Stable 希尔排序 (Shell Sort) n n log2 n n log2 n 1 Not Stable  交换排序 (Exc

转 算法复杂度分析

转自 http://www.cnblogs.com/gaochundong/p/complexity_of_algorithms.html 为什么要进行算法分析? 预测算法所需的资源 计算时间(CPU 消耗) 内存空间(RAM 消耗) 通信时间(带宽消耗) 预测算法的运行时间 在给定输入规模时,所执行的基本操作数量. 或者称为算法复杂度(Algorithm Complexity) 如何衡量算法复杂度? 内存(Memory) 时间(Time) 指令的数量(Number of Steps) 特定操作