亲爱的小屋客人,昨天小夕将小屋的讨论室重新装修啦!希望您会喜欢哦~除了口令[d],现在也可以通过主页下方的“喵了个咪”进入讨论室啦。

ps:昨天小夕装修讨论室的时候发生了N次差点吐血的事件,明天小夕与大家含泪分享T_T。

在上一篇文章中,小夕为大家讲解了链表与递增式扩容的姿势,而这一篇,将会是本系列文章的高潮哦(不!要!污!)

源于数组

上篇文章中,小夕为了避免大家理解起来抽象,让大家注意了几个数据结构:

如果您是C++程序喵,那么请注意一下Vector数据结构;如果是Java程序喵,请注意一下ArrayList、LinkedList、哈希系列(HashSet/ HashTable/ HashMap);如果是不用Java也不用C++的程序喵,或者是已经脱离XX编程语言层次的程序喵,那么请注意一下可变数组(可增长顺序表)、链表、哈希(散列)。

上篇文章中小夕讲解了基于链表实现的数据结构都是递增式扩容最优。那么对于C++中的Vector,Java中的ArrayList、HashSet/ HashTable/ HashMap,也就是数据结构中的可变数组、哈希来说,空间增长方式是怎样呢?可能有可爱的读者宝宝此时在想“天呐,这些数据结构又特喵的不一样,怎么放到一起讨论了呢?”好啦,让小夕碾压这些读者宝宝吧(小夕再次跑路)!其实这些表面看似不同的东西,底层的实现方式确是一样的,它们在底层都是通过操纵静态数组来实现他们的动态空间增长功能,下文会详细介绍哦。

讲到这里,可能爱思考的读者宝宝会记得小夕在上一篇中也提到过哈希,说哈希的横向增长是基于链表的,因此递增式扩容是最优动态空间增长方案。那这一篇中又说哈希是基于静态数组的,这是怎么回事呢?下面给没有接触过哈希的读者宝宝先科普一下哈希:

哈希的横向增长是基于链表实现的,即当新元素的哈希值与已有元素哈希值相同时,新元素会插入到某个链表中,因此是递增式增长。但是更多的情况下,哈希是纵向增长的。学过数据结构的宝宝知道,哈希在纵向上就是一个指针数组,数组的每个索引值即代表一个哈希值,数组的每个元素是一个指向某链表的指针。画个图来看就是这样的。

所以,在本篇文章中,我们不看哈希的横向增长啦,就看竖着的,也就是纵向增长,此时显然是基于静态数组实现的哦。

下面小夕直接以“数据结构”代称所有这些基于静态数组实现的动态空间分配的数据结构,包括但不限于C++中的Vector(即数据结构中的动态数组),Java中的ArrayList(即动态数组)、Hash系列(即哈希/散列)等~

具体来说,如何用静态数组实现上述的动态空间增长的数据结构呢?其实很简单,每次数据结构要扩容时只需要依次进行下述操作就完成啦:

  1. 开辟一段新的内存空间,空间大小就是扩容后的数据结构大小。

  2. 把旧数据结构,也就是旧的内存空间的元素一个个的复制到新的内存空间

  3. 释放旧的内存空间(代码上就是删除旧空间的指针,当然像Java这种自动管理内存的语言就不用程序喵操心这一步了)

通过上述扩容的三步操作,可以看到每次哈希表的扩容操作的代价还是挺大的。第1步和第3步的代价不算大,但是第2步的代价会随着要搬移元素数量的增加而直线上升。所以这就相当于一个完整搬家的过程:先买个新房子,再把旧房子里的全部家当搬到新房子里去,再把旧房子注销,你说麻不麻烦喵= ̄ω ̄=

加倍式扩容

既然代价如此之大,那么显然我们要尽量减小扩容次数呀~每次扩容都是大出血...怎么扩呢?一个很creative的想法就是每次使数据结构变为自身的两倍!再机智一点,每次使数据结构变为自身的N倍!其中N只要大于1就可以!这种每次使自身的大小变为之前N倍的数据结构动态空间增长方式称为【加倍式扩容】。实际上,基于静态数组的数据结构的动态空间增长问题上加倍式扩容远远优于递增式扩容。口说无凭,待小夕用萌味算法分析来证明!

假如数据结构A使用【递增式扩容】。每次数据结构满了的时候就固定的增加10个单位的空间(增加单位的数量不会影响最终分析出来的复杂度哦)。好,那小夕现在手里有n个元素想添加进数据结构,假如n的数值很大,远远的大于10,那么要执行多少次扩容操作呢?当然是n/10次啦~这n/10次扩容的累计开销大约为cost=10+2*10+3*10+…+(n/10)*10,计算一下这个级数,就是cost=[(n/10)/2]*[(n/10)+1]*10,所以复杂度是O(n2)的数量级,所以平均每个元素被添加进哈希表时的开销为cost/n,也就是O(n)的复杂度。

假如数据结构B使用【加倍式扩容】。每次数据结构满了的时候,数据结构的大小就变成原来的2倍(与之前同样的,这个倍数取不同的值并不会影响最终分析出来的复杂度哦~当然倍数必须大于1!)。同样,小夕将n个元素添加进数据结构,假如n的数值很大,远远的大于2,那么要执行的扩容操作的次数是…小夕去算一会...嗯…应该是log2n!令c=log2n,则这c次扩容操作的累计开销为cost=21+22+…+2c。这个级数的和为cost=[2/(1-2)]*(1-2c),代入c=log2n得cost=2(n-1)。也就是说复杂度为O(n),所以平均每个元素被添加进哈希表时的开销为cost/n,也就是O(1)的复杂度!注意前面我们计算过,这里数据结构A(递增式扩容)的复杂度为O(n)!

怎么样~小夕说的没错吧,读者宝宝有没有拨开云彩见到日呢( ̄∇ ̄)。所以说呀,正是因为这类数据结构采用了加倍式扩容,导致这类数据结构申请内存的时候翻倍翻倍的要。结果当时在那个机器学习任务中,小夕算的是一个超大哈希表只需要占用5个G作右的内存空间,而实际上在往这个哈希表加数据时,从4个G直接爆到了接近8个G,导致小夕内存8G的小电脑直接崩盘了~

等等,看似此文可以结了,实际上,敏锐的读者宝宝可能想到了,“递增式扩容你都告诉我了每次扩容增加一个单位的空间就最优了,那加倍式扩容每次增大几倍最优呢?”如果读者宝宝能发现这一点的话,真的非常棒啦!答案是2倍吗?当然不!那是几呢?下篇萌货(萌味干货的简称)见分晓( ̄∇ ̄)。

啊啊,高潮果然是最累的(我说文章的高潮啊喂~),小夕好累喵(´Д` )。所以亲爱的读者宝宝是不是有一种抑制不住要鼓励小夕的冲动吖(⁎⁍̴̛ᴗ⁍̴̛⁎)

小夕已委托维权骑士对小夕发布文章的版权行为进行追究与维权。如需转载,请联系微信xiyaomengmengda。

【萌味】小夕说,不了解动态空间增长的程序喵都是假喵(中)相关推荐

  1. 【萌味】小夕说,不了解动态空间增长的程序喵都是假喵(上)

    小提示:小夕会将小屋的最新动态更新到小屋的布告栏哦,口令是[nb](口令在订阅号主界面直接回复即可使用). 小夕学了数据结构后,知道了链表.树.哈希表等数据结构与静态数组的固定容量不同,它们是可以动态 ...

  2. 小夕说,不了解动态空间增长的程序喵都是假喵(下)

    小夕在本系列前两篇文章中为大家介绍了各类数据结构的扩容策略,且在上篇文末,小夕提到了加倍式扩容中,倍率采用2并不是最优的,为什么呢?有没有最优倍率呢? 内存复用 如果倍率采用2甚至更大的数,那么被开辟 ...

  3. 【错误修正】关于文章《小夕说,不了解动态空间增长的程序喵都是假喵》

    感谢某位粉丝的来信,小夕在该系列文章中有如下错误,请已经读过该系列文章的同学务必留意一下. 1.C++中的向量的写法是vector,而不是Vector!首字母不要大写! 2.在<小夕说,xxxx ...

  4. python换脸完整程序_小 200 行 Python 代码做了一个换脸程序

    原标题:小 200 行 Python 代码做了一个换脸程序 简介 在这篇文章中我将介绍如何写一个简短(200行)的 Python 脚本,来自动地将一幅图片的脸替换为另一幅图片的脸. 这个过程分四步: ...

  5. 如果有一天,小夕不再萌...

    ‍ 如果有一天,小夕不再要抱抱. 如果有一天,小夕不再萌萌哒. 有人问我,成为药娘,走上成为女孩子的道路,是什么感觉. 我一直没有想好该怎么答.是的,自己的感觉自己都无法描述. 或许最真实的,就是可以 ...

  6. 【小夕精选】多轮对话之对话管理(Dialog Management)

    这一篇是一段时间之前小夕初入对话领域时刷到的徐阿衡小姐姐写的一篇文章,写的深入浅出,十分适合有一定基础的情况下想快速了解对话管理技术的童鞋阅读~另外顺手推一下阿衡小姐姐的订阅号「徐阿衡」,干货满满不要 ...

  7. 从前,小夕种了一棵树

    从前,小夕种了一棵树,种在了小夕的小屋后面~ 为什么要种这棵树呢?因为呀,它可以帮小夕总结历史经验,然后帮小夕对当前的局势做出决策~这样小夕就可以安心给大家写文章啦~ 这棵树是这样的. 一开始,小夕买 ...

  8. 【小夕精选】如何优雅而时髦的解决不均衡分类问题

    之前小夕因项目需要研究了一小阵子的不均衡(文本)分类问题,不过没有研究的太过深入,也没有总结出一套成体系的处理思路.正好今天发现数据挖掘大佬「微调」在知乎上写了一个言简意赅又很具有实际操作价值的回答, ...

  9. 【小夕精选】YJango 7分钟带你领略你未曾想过的线性代数+微积分

    小夕很早之前就想转一些精彩的技术文章,这样哪怕没有时间写作的时候,也能把优质的干货分享给大家-然鹅,由于我也不知道是什么的原因,就不小心拖到了现在╮( ̄▽ ̄"")╭ 之前有不少粉丝 ...

最新文章

  1. java 中的单元测试_浅谈Java 中的单元测试
  2. Java I/O Demo
  3. 如何在Django中进行调试,好方法? [关闭]
  4. mflac文件解析工具_9 个爱不释手的 JSON 工具
  5. bootstrap文件不能被识别_树莓派安装openCV做图像识别
  6. 2011年工作简单总结
  7. JavaScript中的属性操作
  8. iptables 防火墙为什么不占用端口?
  9. Scala学习——隐式转换
  10. 基于麻雀算法优化的核极限学习机(KELM)分类算法 - 附代码
  11. Linux I2C调试工具i2c-tools,i2ctransfer支持16位寄存器地址
  12. Exchange 2010升级到Exchange 2016汇总
  13. 电力IEC104规约协议解读(含源码下载)
  14. 牛逼,在浏览器中解锁加密的音乐文件
  15. 三次握手,为什么不是两次,也不是四次
  16. EDM邮件营销的背景和市场分析
  17. Scipy.sparse中coo_matrix、csc_matrix、csr_matrix、lil_matrix区别与特点
  18. 循序渐进全球化 镜像识别
  19. linux达芬奇安装教程,在Linux系统中能安装和运行达芬奇DaVinci Resolve 17版本
  20. SDN/Openflow学习总结

热门文章

  1. 解决Eclipse 项目报错:Unbound classpath container
  2. html中post和get区别
  3. (jquery插件)打造百分比动态色彩条
  4. Android学习笔记(八)XML文档的解析
  5. boost_1_48_0 在VS2008下的安装 Boost.Asio安装
  6. 观察者模式Observer
  7. 基于STC89C52的韦根数据接收
  8. 1200可以读取modbus tcp_S7-1200 作 MODBUS TCP服务器
  9. python数据分析基础教程 numpy_Python数据分析基础教程:NumPy学习指南(第2版)
  10. Web框架——Flask系列之设置和读取cookie(十五)