极客时间已完结课程限时免费阅读

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

04 | 复杂度分析(下):浅析最好、最坏、平均、均摊时间复杂度-极客时间

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

讲述:冯永吉

时长13:31大小12.37M

上一节,我们讲了复杂度的大 O 表示法和几个分析技巧,还举了一些常见复杂度分析的例子,比如 O(1)、O(logn)、O(n)、O(nlogn) 复杂度分析。掌握了这些内容,对于复杂度分析这个知识点,你已经可以到及格线了。但是,我想你肯定不会满足于此。
今天我会继续给你讲四个复杂度分析方面的知识点,最好情况时间复杂度(best case time complexity)、最坏情况时间复杂度(worst case time complexity)、平均情况时间复杂度(average case time complexity)、均摊时间复杂度(amortized time complexity)。如果这几个概念你都能掌握,那对你来说,复杂度分析这部分内容就没什么大问题了。

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

上一节我举的分析复杂度的例子都很简单,今天我们来看一个稍微复杂的。你可以用我上节教你的分析技巧,自己先试着分析一下这段代码的时间复杂度。
// 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;
}
return pos;
}
你应该可以看出来,这段代码要实现的功能是,在一个无序的数组(array)中,查找变量 x 出现的位置。如果没有找到,就返回 -1。按照上节课讲的分析方法,这段代码的复杂度是 O(n),其中,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;
}
这个时候,问题就来了。我们优化完之后,这段代码的时间复杂度还是 O(n) 吗?很显然,咱们上一节讲的分析方法,解决不了这个问题。
因为,要查找的变量 x 可能出现在数组的任意位置。如果数组中第一个元素正好是要查找的变量 x,那就不需要继续遍历剩下的 n-1 个数据了,那时间复杂度就是 O(1)。但如果数组中不存在变量 x,那我们就需要把整个数组都遍历一遍,时间复杂度就成了 O(n)。所以,不同的情况下,这段代码的时间复杂度是不一样的。
为了表示代码在不同情况下的不同时间复杂度,我们需要引入三个概念:最好情况时间复杂度、最坏情况时间复杂度和平均情况时间复杂度。
顾名思义,最好情况时间复杂度就是,在最理想的情况下,执行这段代码的时间复杂度。就像我们刚刚讲到的,在最理想的情况下,要查找的变量 x 正好是数组的第一个元素,这个时候对应的时间复杂度就是最好情况时间复杂度。
同理,最坏情况时间复杂度就是,在最糟糕的情况下,执行这段代码的时间复杂度。就像刚举的那个例子,如果数组中没有要查找的变量 x,我们需要把整个数组都遍历一遍才行,所以这种最糟糕情况下对应的时间复杂度就是最坏情况时间复杂度。

平均情况时间复杂度

我们都知道,最好情况时间复杂度和最坏情况时间复杂度对应的都是极端情况下的代码复杂度,发生的概率其实并不大。为了更好地表示平均情况下的复杂度,我们需要引入另一个概念:平均情况时间复杂度,后面我简称为平均时间复杂度。
平均时间复杂度又该怎么分析呢?我还是借助刚才查找变量 x 的例子来给你解释。
要查找的变量 x 在数组中的位置,有 n+1 种情况:在数组的 0~n-1 位置中不在数组中。我们把每种情况下,查找需要遍历的元素个数累加起来,然后再除以 n+1,就可以得到需要遍历的元素个数的平均值,即:
我们知道,时间复杂度的大 O 标记法中,可以省略掉系数、低阶、常量,所以,咱们把刚刚这个公式简化之后,得到的平均时间复杂度就是 O(n)。
这个结论虽然是正确的,但是计算过程稍微有点儿问题。究竟是什么问题呢?我们刚讲的这 n+1 种情况,出现的概率并不是一样的。我带你具体分析一下。(这里要稍微用到一点儿概率论的知识,不过非常简单,你不用担心。)
我们知道,要查找的变量 x,要么在数组里,要么就不在数组里。这两种情况对应的概率统计起来很麻烦,为了方便你理解,我们假设在数组中与不在数组中的概率都为 1/2。另外,要查找的数据出现在 0~n-1 这 n 个位置的概率也是一样的,为 1/n。所以,根据概率乘法法则,要查找的数据出现在 0~n-1 中任意位置的概率就是 1/(2n)。
因此,前面的推导过程中存在的最大问题就是,没有将各种情况发生的概率考虑进去。如果我们把每种情况发生的概率也考虑进去,那平均时间复杂度的计算过程就变成了这样:
这个值就是概率论中的加权平均值,也叫作期望值,所以平均时间复杂度的全称应该叫加权平均时间复杂度或者期望时间复杂度
引入概率之后,前面那段代码的加权平均值为 (3n+1)/4。用大 O 表示法来表示,去掉系数和常量,这段代码的加权平均时间复杂度仍然是 O(n)。
你可能会说,平均时间复杂度分析好复杂啊,还要涉及概率论的知识。实际上,在大多数情况下,我们并不需要区分最好、最坏、平均情况时间复杂度三种情况。像我们上一节课举的那些例子那样,很多时候,我们使用一个复杂度就可以满足需求了。只有同一块代码在不同的情况下,时间复杂度有量级的差距,我们才会使用这三种复杂度表示法来区分。

均摊时间复杂度

到此为止,你应该已经掌握了算法复杂度分析的大部分内容了。下面我要给你讲一个更加高级的概念,均摊时间复杂度,以及它对应的分析方法,摊还分析(或者叫平摊分析)。
均摊时间复杂度,听起来跟平均时间复杂度有点儿像。对于初学者来说,这两个概念确实非常容易弄混。我前面说了,大部分情况下,我们并不需要区分最好、最坏、平均三种复杂度。平均复杂度只在某些特殊情况下才会用到,而均摊时间复杂度应用的场景比它更加特殊、更加有限。
老规矩,我还是借助一个具体的例子来帮助你理解。(当然,这个例子只是我为了方便讲解想出来的,实际上没人会这么写。)
// 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() 的例子和前面那个 find() 的例子,你就会发现这两者有很大差别。
首先,find() 函数在极端情况下,复杂度才为 O(1)。但 insert() 在大部分情况下,时间复杂度都为 O(1)。只有个别情况下,复杂度才比较高,为 O(n)。这是 insert()第一个区别于 find() 的地方。
我们再来看第二个不同的地方。对于 insert() 函数来说,O(1) 时间复杂度的插入和 O(n) 时间复杂度的插入,出现的频率是非常有规律的,而且有一定的前后时序关系,一般都是一个 O(n) 插入之后,紧跟着 n-1 个 O(1) 的插入操作,循环往复。
所以,针对这样一种特殊场景的复杂度分析,我们并不需要像之前讲平均复杂度分析方法那样,找出所有的输入情况及相应的发生概率,然后再计算加权平均值。
针对这种特殊的场景,我们引入了一种更加简单的分析方法:摊还分析法,通过摊还分析得到的时间复杂度我们起了一个名字,叫均摊时间复杂度
那究竟如何使用摊还分析法来分析算法的均摊时间复杂度呢?
我们还是继续看在数组中插入数据的这个例子。每一次 O(n) 的插入操作,都会跟着 n-1 次 O(1) 的插入操作,所以把耗时多的那次操作均摊到接下来的 n-1 次耗时少的操作上,均摊下来,这一组连续的操作的均摊时间复杂度就是 O(1)。这就是均摊分析的大致思路。你都理解了吗?
均摊时间复杂度和摊还分析应用场景比较特殊,所以我们并不会经常用到。为了方便你理解、记忆,我这里简单总结一下它们的应用场景。如果你遇到了,知道是怎么回事儿就行了。
对一个数据结构进行一组连续操作中,大部分情况下时间复杂度都很低,只有个别情况下时间复杂度比较高,而且这些操作之间存在前后连贯的时序关系,这个时候,我们就可以将这一组操作放在一块儿分析,看是否能将较高时间复杂度那次操作的耗时,平摊到其他那些时间复杂度比较低的操作上。而且,在能够应用均摊时间复杂度分析的场合,一般均摊时间复杂度就等于最好情况时间复杂度。
尽管很多数据结构和算法书籍都花了很大力气来区分平均时间复杂度和均摊时间复杂度,但其实我个人认为,均摊时间复杂度就是一种特殊的平均时间复杂度,我们没必要花太多精力去区分它们。你最应该掌握的是它的分析方法,摊还分析。至于分析出来的结果是叫平均还是叫均摊,这只是个说法,并不重要。

内容小结

今天我们学习了几个复杂度分析相关的概念,分别有:最好情况时间复杂度、最坏情况时间复杂度、平均情况时间复杂度、均摊时间复杂度。之所以引入这几个复杂度概念,是因为,同一段代码,在不同输入的情况下,复杂度量级有可能是不一样的。
在引入这几个概念之后,我们可以更加全面地表示一段代码的执行效率。而且,这几个概念理解起来都不难。最好、最坏情况下的时间复杂度分析起来比较简单,但平均、均摊两个复杂度分析相对比较复杂。如果你觉得理解得还不是很深入,不用担心,在后续具体的数据结构和算法学习中,我们可以继续慢慢实践!

课后思考

我们今天学的几个复杂度分析方法,你都掌握了吗?你可以用今天学习的知识,来分析一下下面这个 add() 函数的时间复杂度。
// 全局变量,大小为10的数组array,长度len,下标i。
int array[] = new int[10];
int len = 10;
int i = 0;
// 往数组中添加一个元素
void add(int element) {
if (i >= len) { // 数组空间不够了
// 重新申请一个2倍大小的数组空间
int new_array[] = new int[len*2];
// 把原来array数组中的数据依次copy到new_array
for (int j = 0; j < len; ++j) {
new_array[j] = array[j];
}
// new_array复制给array,array现在大小就是2倍len了
array = new_array;
len = 2 * len;
}
// 将element放到下标为i的位置,下标i加一
array[i] = element;
++i;
}
欢迎留言和我分享,我会第一时间给你反馈。
分享给需要的人,Ta购买本课程,你将得20
生成海报并分享

赞 350

提建议

上一篇
03 | 复杂度分析(上):如何分析、统计算法的执行效率和资源消耗?
下一篇
05 | 数组:为什么很多编程语言中数组都从0开始编号?
unpreview
 写留言

精选留言(1170)

  • Alvin
    2018-09-28
    老师讲的很好,练习题最好是O(1),最差是O(n), 均摊是O(1)。 看到好多人纠结于清空数组的问题: 对于可反复读写的存储空间,使用者认为它是空的它就是空的。如果你定义清空是全部重写为0或者某个值,那也可以!但是老师举的例子完全没必要啊!写某个值和写任意值在这里有区别吗,使用值只关心要存的新值!所以老师的例子,清空把下标指到第一个位置就可以了!
    展开

    作者回复: 嗯嗯 是的 多谢你。同学们帮把这一条顶上去吧 可以让其他同学都看看

    共 30 条评论
    2510
  • 阿杜S考特
    2018-09-28
    当i < len时, 即 i = 0,1,2,...,n-1的时候,for循环不走,所以这n次的时间复杂度都是O(1); 当i >= len时, 即 i = n的时候,for循环进行数组的copy,所以只有这1次的时间复杂度是O(n); 由此可知: 该算法的最好情况时间复杂度(best case time complexity)为O(1); 最坏情况时间复杂度(worst case time complexity)为O(n); 平均情况时间复杂度(average case time complexity), 第一种计算方式: (1+1+...+1+n)/(n+1) = 2n/(n+1) 【注: 式子中1+1+...+1中有n个1】,所以平均复杂度为O(1); 第二种计算方式(加权平均法,又称期望): 1*(1/n+1)+1*(1/n+1)+...+1*(1/n+1)+n*(1/(n+1))=1,所以加权平均时间复杂度为O(1); 第三种计算方式(均摊时间复杂度): 前n个操作复杂度都是O(1),第n+1次操作的复杂度是O(n),所以把最后一次的复杂度分摊到前n次上,那么均摊下来每次操作的复杂度为O(1)
    展开
    共 54 条评论
    886
  • 好吃二师兄
    2018-09-29
    最好是O(1),最坏是O(n),平均平摊是O(1). 不要纠结add和insert在哪儿被调用了。。。代码都写出来反而不好看。 个人体会: 平均和平摊基本就是一个概念,平摊是特殊的平均。在分析时间复杂度是O(1)还是O(n)的时候最简单就是凭感觉,,,,,,,,出现O(1)的次数远大于出现O(n)出现的次数,那么平均平摊时间复杂度就是O(1)。。。。
    展开

    作者回复: 留言看似很平淡 但透漏着高手的气息。说的没错。高手就是凭感觉👍

    共 19 条评论
    612
  • 姜威
    2018-09-28
    总结 一、复杂度分析的4个概念 1.最坏情况时间复杂度:代码在最理想情况下执行的时间复杂度。 2.最好情况时间复杂度:代码在最坏情况下执行的时间复杂度。 3.平均时间复杂度:用代码在所有情况下执行的次数的加权平均值表示。 4.均摊时间复杂度:在代码执行的所有复杂度情况中绝大部分是低级别的复杂度,个别情况是高级别复杂度且发生具有时序关系时,可以将个别高级别复杂度均摊到低级别复杂度上。基本上均摊结果就等于低级别复杂度。 二、为什么要引入这4个概念? 1.同一段代码在不同情况下时间复杂度会出现量级差异,为了更全面,更准确的描述代码的时间复杂度,所以引入这4个概念。 2.代码复杂度在不同情况下出现量级差别时才需要区别这四种复杂度。大多数情况下,是不需要区别分析它们的。 三、如何分析平均、均摊时间复杂度? 1.平均时间复杂度 代码在不同情况下复杂度出现量级差别,则用代码所有可能情况下执行次数的加权平均值表示。 2.均摊时间复杂度 两个条件满足时使用:1)代码在绝大多数情况下是低级别复杂度,只有极少数情况是高级别复杂度;2)低级别和高级别复杂度出现具有时序规律。均摊结果一般都等于低级别复杂度。
    展开
    共 14 条评论
    395
  • 赤身马可
    2018-09-29
    报告老师,我好像走错了教室。 我是一个文科转行过来的菜鸟,刚刚学完Python,基本搞懂了“遍历”、“循环”、“判断”等概念。 您开篇讲的课,我都基本都能明白,也提起了兴趣和信心,准备好好跟您学习。但这两次课听完,我又晕菜了。 想请问一下,如果听不太懂(也可以去掉“太”),需要补哪些课?您能告诉我进入您课程的坡道和垫脚石么?有没有稍低一点年级的资料,让我可以补补课呢? 还请抽时间回答,谢谢。
    展开

    作者回复: 嗯嗯,同学你好. 你说了刚学完python,可能代码还没写熟练,所以我建议把python书上的所有实例代码都自己敲一遍,默写一遍。学编程,光看不写肯定是不行的。 等你python代码写熟练了,你可以再开始学我这个专栏。 因为你没有数据结构和算法的基础,所以我建议,配合着《大话数据结构》《算法图解》两本书一块来学习。 学习这个专栏的过程中,你可以把我讲到的数据结构和算法都用python代码实现一遍,如果实现不了,可以参照我放在Github上的代码,自己看懂之后,默写一遍。这个步骤非常锻炼你的编程能力,不要忽视! 在学习专栏的过程中,不要一觉得看不懂就放弃,师傅领进门,修行靠个人。这里没有葵花宝典一样的捷径。学习还要靠自己。看不懂?那就自己多百度一下,看不懂也可以问问你同学、同事、学长,用一个星期来看一篇文章,狠下心来,别怕麻烦,不会学不会的。 还有很多时候看不懂,你就硬着头皮看,都看完一遍,就会有感觉。之后再等有空了,再来看一遍,慢慢的都懂了。这门课很难,对于初学者来说,应该是计算机里最难的之一了,所以不要期望轻松就学会,这是不现实的。

    共 14 条评论
    273
  • jon
    2018-10-01
    看了大家的留言总结的很好,自己把练习题的答案整理了一下与大家分享: 1. 最好情况时间复杂度为 O(1) 2.最坏情况分析: 最坏情况代码执行的次数跟每次数组的长度有关 第1次调用insert的执行的次数为 n , 第2次调用insert的执行的次数为 2n , 第3次调用insert的执行的次数为 2^2 * n 第k次调用insert的执行的次数为 2^(k-1) * n 最坏时间复杂度为 O(n)。 3. 平均情况分析 当每次遇到最坏情况时数组会进行2倍扩容,原数组被导入新数组,虽然数组的长度变大了,但是插入操作落在的区间的长度是一样的,分别是0~len-1, len~(2len-1),....; 插入的情况仍是len+1种:0~len-1和插满之后的O(len);所以每次插入的概率是:p= 1/len+1, 最后求出加权平均时间复杂度为 1*p + 2*p+ ▪▪▪ + len*p + len * p = O(1) ; 4. 均摊时间复杂度 O(1) 而均摊复杂度由于每次O(len)的出现都跟着len次O(1),是前后连贯的,因而将O(len)平摊到前len次上,得出平摊复杂度是O(1)
    展开

    作者回复: 👍

    共 22 条评论
    174
  • 天天向上
    2018-12-28
    高中的数学已经忘光了,又回去补了下, 现在将推导的结果分享给大家 1、1+2+3+....+n+n 首先知道这是一个等差数列,等差数列的定义:每一项与前一个项的差都等于一个常数,这就是等差数列 2、计算等差数列的求和公式: n(1+n)/2 ,这是一个已经推导出来的公式,至于怎么推导出来的,自行去学习吧 好了, 现在开始推导: 1、主要就是推导分子,1+2+3+...+n+n = n(1+n)/2 + n = n(1+n)+2n /2 = n+n²+2n/2 = n² + 3n /2 = n(n+3)/2 2、将 n(n+3)/2 代入式子中,就成了 n(n+3)/2 / n+1 = n(n+3) /2(n+1) 好了,打完收功
    展开
    共 23 条评论
    125
  • 蝴蝶
    2018-09-28
    insert方法中有清空数组吗?抱歉,能指出哪行吗?真不明白😂

    作者回复: count=1;count被重置为1。之后再插入的数据就会覆盖掉原来的数据。就相当于将原数组清空了。并不需要显示的去清空

    共 6 条评论
    113
  • Kealina.
    2018-10-04
    调皮一下,还请老师来衡量下这例子恰当不。 举个栗子🌰: 今天你准备去老王家拜访下,可惜老王的爱人叫他去打个酱油,她告诉你说她限时n分钟🕒给他去买。那么你想着以他家到楼下小卖部来回最多一分钟,那么 “最好的情况”就是你只用等他一分钟。那么也有可能遇到突发情况,比如说电梯人多吖,路上摔了一胶,天知道他去干了什么,用了n分钟,没办法👐,主上有令,n分钟限时,那这就是“最坏的情况”。难点,平均时间复杂度 就是他有可能是第1.2.3...n,中的某个分钟回来,那平均就是1+2+3+...n/n,把 所有可能出现的情况的时间复杂度 相加除以 情况数 。均摊的话就是把花时间多的分给花时间少的,得到一个中间值,所以说这就会和平均混淆,个人觉得主要还是概念不同。假如n是10分钟,那么9分钟分4分钟到1分钟那,8分3给2...,那均摊下来就是5分钟.编不下去了🤦🏼‍♀️
    展开

    作者回复: 哈哈,写的太好了。留言区卧虎藏龙啊~

    共 10 条评论
    104
  • Stalary
    2018-09-28
    递归的时间复杂度怎么算呀

    作者回复: 这个话题有点大 要具体看了 重点应该分析递归调用的次数吧。然后再看每次调用的耗时。综合考虑

    共 4 条评论
    79
  • 张三丰
    2018-09-29
    1+2+3....+n+n / n+1 = n(n+3)/2(n+1) 老师这个公式怎么推导出来的 能一步步展示下吗

    作者回复: 公式是求平均比对多少个数组元素才能找到x。如果x再第一个位置,那需要1次比对,如果再第二个位置,就需要比对2次,一次类推,如果在第n个位置,就需要比对n次。如果不在数组中,也需要比对n次。所有的次数之和除以n+1中情况,就是平均比对元素个数。

    共 9 条评论
    53
  • ppingfann
    2018-09-28
    课后题的最坏时间复杂度不应该是O(1)吗?按照上一节讲的,循环的次数如果是有限次,就算数量极大,那么也应该是O(1)不是吗? 如果答案如大家所说的是O(n),那么原题的len=10这个初始条件就应该改写为len=n。

    作者回复: 因为len并不是个确定量 初始值是10而已

    共 8 条评论
    42
  • Silence
    2018-09-28
    老师,加权平均值那个公式是怎么来的,每个的概率都是 1/2n,平均的不应该也是 1/2n 吗?为什么后面成了 2*(1/2n)+3*(1/2n)+.....n*(1/2n)+n*(1/2)
    共 6 条评论
    42
  • leo
    2018-09-29
    画的前两节思维导图: https://share.weiyun.com/5D2VFqS

    作者回复: 👍

    共 2 条评论
    31
  • 冷颜〆
    2019-02-26
    // 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=0 怎么看时间复杂度都是O(1)啊 除非外面有循环一直运行 所以这段一直理解不了
    展开

    作者回复: 是一直循环调用insert

    共 11 条评论
    25
  • 刘浩
    2018-09-29
    这道题按照老师所讲的 答案是 O(1),每次扩容的数量都是原来的2倍,都是经历之前数组长度的次数再次进行扩容,所以完全被均摊开了。 但是老师我有一个问题,就是按照您讲的确实时间复杂度被均摊成了O(1),在这是一个理论的平均值,但终究不能忽略O(n)的存在,当n到达一定量级的时候,这个风险还是存在的,如果把他等同于O(1),真的没关系吗?
    展开

    作者回复: 你理解的很对啊,均摊只是其中一种复杂度度量方法,并不是说我们只关注均摊,不关注最坏。我们评价一段代码或者算法的时候,还是会综合这几种复杂度的。用什么表示复杂度不重要,初衷还是能更好的体现出这个算法或者代码的性能。

    24
  • molybdenum
    2018-09-29
    答案与add例子相同, 至于大家纠结的清空问题,可以看做是标记清除,在同一地址空间上再写上新的值即可,没有必要硬删除,再开辟空间,或者名义上重置成某个数,直接用新的值覆盖即可

    作者回复: 是的 因为有些编程基础比较差的同学 他可能之前学清空就是一个一个的删 或者置为0

    16
  • 厚厚
    2018-09-28
    第二个例子中,为什么是n+1次遍历?
    共 8 条评论
    13
  • 蝴蝶
    2018-09-28
    我算了下,最小是O(1),最大是O(n),平均和分摊都是O(1),对吗?😀

    作者回复: 是的 分析正确。不过我们一般情况下平均 均摊说一个就好了

    共 2 条评论
    13
  • aof
    2018-09-28
    最大的疑惑就是,insert()方法和add()方法是如何被调用的???
    共 6 条评论
    12