数组问题
Varsion

随机访问

数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

线性表:

线性表就是数据排成像一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。

除了数组,链表、队列、栈等也是线性表结构。

和线性表相反的改建是非线性表:二叉树、堆、图等。之所以叫非线性,是因为,在非线性表中,数据之间并不是简单的前后关系。

连续的内存空间和相同类型的数据:

正因为’线性表’和’连续的内存空间和相同类型的数据’,使得数组具有了随机访问的特性。

这两个限制也让数组的很多操作变得非常低效,比如要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。

“链表适合插入、删除,时间复杂度 O(1);数组适合查找,查找时间复杂度为 O(1)”。

实际上,这种表述是不准确的。数组是适合查找操作,但是查找的时间复杂度并不为 ·、O(1)。即便是排好序的数组,你用二分查找,时间复杂度也是 O(logn)。所以,正确的表述应该是,数组支持随机访问,根据下标随机访问的时间复杂度为 O(1)

低效的’插入’、’删除’

插入操作

假设数组的长度为 n,现在,如果需要将一个数据插入到数组中的第 k 个位置。为了把第 k 个位置腾出来,给新来的数据,我们需要将第 k~n 这部分的元素都顺序地往后挪一位。

果在数组的末尾插入元素,那就不需要移动数据了,这时的时间复杂度为 O(1)。但如果在数组的开头插入元素,那所有的数据都需要依次往后移动一位,所以最坏时间复杂度是 O(n)。 因为我们在每个位置插入元素的概率是一样的,所以平均情况时间复杂度为 (1+2+…n)/n=O(n)。

如果数组中的数据是有序的,在某个位置插入一个新的元素时,就搬移 k 之后的数据。但是,如果数组中存储的数据并没有任何规律,数组只是被当作一个存储数据的无序集合。在这种情况下,如果要将某个数据插入到第 k 个位置,为了避免大规模的数据搬移,还有一个简单的办法就是,直接将第 k 位的数据搬移到数组元素的最后,把新的元素直接放入第 k 个位置。

删除操作

如果要删除第 k 个位置的数据,为了内存的连续性,也需要搬移数据,不然中间就会出现空洞,内存就不连续了。

和插入类似,如果删除数组末尾的数据,则最好情况时间复杂度为 O(1);如果删除开头的数据,则最坏情况时间复杂度为 O(n);平均情况时间复杂度也为 O(n)。

实际上,在某些特殊场景下,并不一定非得追求数组中数据的连续性。如果将多次删除操作集中在一起执行,删除效率会提高的很多

可以先记录下已经删除的数据。每次的删除操作并不是真正地搬移数据,只是记录数据已经被删除。当数组没有更多空间存储数据时,我们再触发执行一次真正的删除操作,这样就大大减少了删除操作导致的数据搬移。

警惕数组越界

简而言之,数组越界会给项目带来莫名其妙的逻辑错误,很难去调试,debug难度是很大的。

当然,并非所有语言都像C一样,把数组越界检查的工作丢给程序员来做。

容器

对于业务开发,直接使用容器就足够了,省时省力。毕竟损耗一丢丢性能,完全不会影响到系统整体的性能。

但如果做一些非常底层的开发,比如开发网络框架,性能的优化需要做到极致,这个时候数组就会优于容器,成为首选


  • Post title:数组问题
  • Post author:Varsion
  • Create time:2020-07-30 21:58:14
  • Post link:https://blog.varsion.cn/post/6856af34.html
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
Comments