JavaScript 算法之最好、最坏时间复杂度分析

栏目: 编程工具 · 发布时间: 5年前

内容简介:上一篇文章中介绍了复杂度的分析,相信小伙伴们对常见代码的时间或者空间复杂度肯定能分析出来了。话不多说,出个题目考考大家,分析下面代码的时间复杂度(ps: 虽然说并不会这么写)上面函数的功能就是查找一个变量 x 是否在 数组 arr 中,如果在的话,返回所在的位置,否则就返回 -1。通过上一节的学习分析,这个函数的时间复杂度就很容易知道了,为 O(n)。

上一篇文章中介绍了复杂度的分析,相信小伙伴们对常见代码的时间或者空间复杂度肯定能分析出来了。

思考测试

话不多说,出个题目考考大家,分析下面代码的时间复杂度(ps: 虽然说并不会这么写)

function find(n, x, arr) {
        let ind = -1;
        for (let i = 0; i < n; i++) {
          if (arr[i] === x) ind = i;
        }
        return ind;
      }
复制代码

上面函数的功能就是查找一个变量 x 是否在 数组 arr 中,如果在的话,返回所在的位置,否则就返回 -1。通过上一节的学习分析,这个函数的时间复杂度就很容易知道了,为 O(n)。

接下来,稍微优化下这个 find 函数,如果查找到目标的话,就没必要再往后查找了。

请分析下优化后函数的时间复杂度:

function find(n, x, arr) {
        let ind = -1;
        for (let i = 0; i < n; i++) {
          if (arr[i] === x) ind = i;
          break;
        }
        return ind;
      }
复制代码

现在代码的时间复杂度还为 O(n)吗?不确定,利用上一章的分析法就无法解决了。

不同情况

因为要查找的变量 x 可能会出现在数组的任意位置。如果变量 x 恰好是数组中第一个元素,那么函数就会 break ,后续就不会继续遍历了,那时间复杂度就是 O(1)。但如果恰好是数组中第末个元素,或者数组中不存在变量 x 的话,那就需要把整个数组都遍历一遍,时间复杂度就成了 O(n)。所以,不同的情况下,这个函数的时间复杂度是不一样的。

那为了表示代码在不同情况下的不同时间复杂度,需要了解以下三个概念:最好情况时间复杂度、最坏情况时间复杂度和平均情况时间复杂度**。
复制代码

理想情况

最好情况时间复杂度:在最理想的情况下,执行这段代码的时间复杂度。比如说刚才那段函数,在最理想的情况下,要查找的变量 x 正好是数组的第一个元素,这种情况下对应的时间复杂度就是 最好情况时间复杂度

糟糕情况

最坏情况时间复杂度:在最糟糕的情况下,执行这段代码的时间复杂度。比如说刚才那段函数,要查找的变量 x 正好是数组的第末个元素或者不在数组中存在 ,查找函数就会把数组都遍历一遍,这种情况下对应的时间复杂度就是 最坏情况时间复杂度

平均情况

但是, 最好情况时间复杂度最坏情况时间复杂度 对应的都是极端情况下的代码复杂度,发生的概率其实并不大。

为了更好地表示平均情况下的复杂度,引入另一个概念:平均情况时间复杂度,简称为平均时间复杂度。

那如何分析平均时间复杂度呢,还是拿刚才那段查找函数来说:

要查找的变量 x 在数组中的位置,有 n+1 种情况:在数组的 0~n-1 位置中和不在数组中。然后把每种情况下,查找需要遍历的元素个数累加起来,然后再除以 n+1,就可以得到需要遍历的元素个数的平均值。

JavaScript 算法之最好、最坏时间复杂度分析

根据上章所说,时间复杂度的大 O 标记法中,可以省略掉系数、低阶、常量,所以,把这个公式简化之后,得到的 平均时间复杂度 就是 O(n)。

但是上面计算的过程中,没有考虑到概率的问题,因为出现在每个位置的概率是不一样的,所以得重新计算,如下分析:

要查找的变量 x,要么在数组里,要么就不在数组里。简单标记这两种情况下的概率都为 1/2。另外,要查找的数据出现在 0~n-1 这 n 个位置的概率也是一样的,为 1/n。所以,根据概率乘法法则,要查找的数据出现在 0~n-1 中任意位置的概率就是 1/(2n)。那我们把每种情况发生的概率都考虑进去,计算表达式就变成了:

JavaScript 算法之最好、最坏时间复杂度分析

最后的结果也叫做概率中的 加权平均值 ,那最后此段函数的 平均时间复杂度 就为 O(n)。

这么看, 平均时间复杂度 是不是好麻烦,还需要概率计算。实际上,在大多数情况下,我们并不需要区分最好、最坏、平均情况时间复杂度三种情况。很多时候,我们使用一个复杂度就可以满足需求了。只有同一块代码在不同的情况下,时间复杂度有量级的差距,我们才会使用这三种复杂度表示法来区分。

均摊情况

接下来再看一个概念,特殊的平均时间复杂度:均摊时间复杂度。

先来看一个特殊的函数,分析下它的时间复杂度:

{
      var arr = new Array(n); // n 代表任意数字
      var ind = 0;
      function add(num) {
        if (ind === arr.length) {
          var sum = 0;
          for (var i = 0; i < arr.length; i++) {
            sum += arr[i];
          }
          arr[0] = sum;
          ind = 1;
        }
        arr[ind] = num;
        ind++;
      }
    }
复制代码

add 函数就是实现一个往数组中添加数据的功能。先定义一个任意长度的空数组,然后给数组添加数据。当达到数组长度后,也就是 ind === array.length 时,用 for 循环遍历数组求和,将求和之后的 sum 值放到数组的第一个位置,然后再将新的数据插入。但如果数组一开始就有空的话,则直接将数据添加到数组中。

来分析下此函数的时间复杂度:

最理想的情况下,数组中有剩余位置,我们只需要将数据添加到数组下标为 ind 的位置就可以了,所以 最好情况时间复杂度 为 O(1)。 最糟糕的情况下,数组中没有剩余位置,我们需要先做一次数组的遍历求和,然后再添加数据,所以 最坏情况时间复杂度 为 O(n)。

接下来分析需要计算的 平均时间复杂度

由于数组的长度是 n,根据数据添加的位置的不同,可以分为 n 种情况,每种情况的时间复杂度是 O(1)。除此之外,还有一种特殊的情况,就是在数组没有空闲空间时添加一个数据,这个时候的时间复杂度是 O(n)。而且,这 n+1 种情况发生的概率一样,都是 1/(n+1)。

JavaScript 算法之最好、最坏时间复杂度分析

所以根据大 O 表示法, 平均时间复杂度 就为 O(1)。

其实 add 函数的平均复杂度不需要这么复杂,接下来我们看看 find 函数和 add 函数的区别:

  • find 函数在极端情况下,时间复杂度才为 O(1)。但 add 函数在大部分情况下,时间复杂度都为 O(1)。只有个别情况下,时间复杂度才比较高,为 O(n)。
  • 对于 add 函数来说,O(1) 时间复杂度的添加和 O(n) 时间复杂度的添加,出现的频率是非常有规律的,而且有一定的前后顺序,一般都是一个 O(n) 添加之后,紧跟着 n-1 个 O(1) 的添加操作,循环往复。

所以,针对这样一种特殊场景的复杂度分析,我们并不需要像之前讲平均复杂度分析方法那样,找出所有的输入情况及相应的发生概率,然后再计算加权平均值。

针对这种特殊的情况,我们引入了一种更加简单的分析方法: 摊还分析法 。通过摊还分析得到的时间复杂度,叫 均摊时间复杂度

那如何使用摊还分析法来分析算法的 均摊时间复杂度 呢?

还是看 add 函数。每一次 O(n) 的添加操作,都会跟着 n-1 次 O(1) 的添加操作,所以把耗时多的那次操作均摊到接下来的 n-1 次耗时少的操作上,均摊下来,这一组连续的操作的均摊时间复杂度就是 O(1)。这就是 均摊分析 的大致方法。

一般情况总结为:

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

生活举例

看高人如何把复杂度利用到生活中:

今天你准备去老王家拜访下,可惜老王的爱人叫他去打个酱油,她告诉你说她限时 n 分钟给他去买。

那么你想着以他家到楼下小卖部来回最多一分钟,那么 “最好的情况”就是你只用等他一分钟。

那么也有可能遇到突发情况,比如说电梯没电了,或者路上摔了一跤,天知道他去干了什么,用了 n 分钟。没办法,老婆有令,n 分钟限时,那这就是“最坏的情况”。

那“平均时间复杂度” 就是他有可能是第 1,2,3,...,n 中的某个分钟回来,那平均就是 1+2+3+...n/n,把 所有可能出现的情况的时间复杂度 相加除以情况数 。

“均摊时间复杂度”的话就是把花时间多的分给花时间少的,得到一个中间值。假如 n 是 10 分钟,那么 9 分钟分 4 分钟到 1 分钟那,8 分 3 给 2...,那均摊下来就是 5 分钟。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

微信民族志、自媒体时代的知识生产与文化实践

微信民族志、自媒体时代的知识生产与文化实践

赵旭东 / 中国社会科学出版社 / 2017-9 / 98.00元

进入二十一世纪以来,随着网络技术的发展,自媒体的悄然登场深度影响着我们的日常生活。中国社会中自媒体通讯方式的普及以及随之而有的一种文化书写的新形式——微信民族志的出现使原有文化秩序中时空意义发生转变的同时,也在重新塑造着以研究异文化为己任的人类学学科自身的成长、转型与发展。在此种情境之下,由中国人民大学人类学研究所、中国人民大学国家发展与战略研究院、中国人民大学社会学理论与方法研究中心、《探索与争......一起来看看 《微信民族志、自媒体时代的知识生产与文化实践》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码