顺序表和链表都属于是基本的数据结构,都是线性表的一种,他们在逻辑上每个数据都是按照一定的顺序来进行存放的。
可以从以下六个方面对顺序表和链表进行比较:存储空间、随机访问性、在任意位置插入或删除元素、添加元素、应用场景、缓存利用率。
目录
一、存储空间
从存储空间上看顺序表在逻辑和物理空间上一定是连续;链表在逻辑上一定是连续的,但在物理空间上不一定是连续的。
顺序表使用的底层逻辑是数组,数组的概念就是一个固定长度的存储相同数据类型的数据结构,数组中的元素被存储在一段连续的内存空间中。所以顺序表在物理上一定是连续的。
而链表是由一个一个的节点拼接起来,用指针相互联系起来的,但每个节点都是在我们需要的时候才申请的,因此每个节点的位置都不是固定的,他们在内存中的位置是没有固定的顺序的,因此链表在物理上不一定是连续的。
二、随机访问性
顺序表的本质就是一个长度可变的数组,因此我们可以通过数组的下标达到随机访问某个位置上的数据。
链表之间联系是通过指针来实现的,它并没有所谓的下标,要找到某个位置的数据,只能按照顺序进行访问,不能直接找到某个位置存储的数据。
三、在任意位置插入或删除元素
顺序表在中间的某个位置插入或者删除元素时,需要将挪动多个元素,效率很低。
链表在中间的某个位置插入或者删除元素时,只需要改变指针的指向,效率很高。
四、添加元素
顺序表在插入元素时,需要考虑容量的问题,如果容量不够,需要扩容,而扩容是有时间消耗的,同时扩容以后,不是所有的空间都会被利用到,还可能造成空间浪费的问题。
链表是在需要插入元素的时候才会申请相应的空间,所以不存在容量和空间浪费的问题,但同时,每个元素在插入以前都需要申请一个新的空间,所以总的来说,插入元素时链表的效率是比顺序表要稍微差一些的。
五、应用场景
如果需要我们快速的存储数据并且能够快速访问指定位置的数据时,我们应当使用顺序表。
当我们需要再任意位置频繁的插入或者删除元素时,我们采用链表会更合适一些。
六、缓存利用率
为了解决计算机的CPU和主存速度不匹配的问题,在CPU和主存之间存在一个叫做Cache的高速缓存;Cache的速度很快,但是容量很小,如果我们要调用的数据正好在Cache中,我们就叫做命中,当数据不再Cache中时,会把数据先调入Cache中,我们就叫做不命中;因为我们在使用数据时,很多时候都会用到这个数据周围的其他数据,所以为了提高Cache的命中率,当Cache不命中时,不仅会把这个数据调入Cache中,还会把这个数据周围的一些数据调入Cache。
基于这个特性,我们知道顺序表在内存中是连续存放的,所以当我们调用一次这个顺序表中的数据时,可能这个顺序表的大部分数据都被调入了Cache,这样再调用其他数据时,CPU的访问效率就高了,我们就称顺序表的缓存利用率高。
同时,链表在内存中是随机存放的,当我们第一次访问这个链表时,会把当前节点附件的数据都放入Cache,但是其他节点不一定在这个节点的附近,可能和这个节点的位置很远,并没有被放入Cache中,所以当我们又要访问这个链表中的其他元素时,可能又要把这个节点附件的数据都调入Cache,因此链表的缓存利用率就低了。不仅如此,因为Cache的大小是很有限的,访问链表的时候不仅不能提高CPU的速度,还很有可能会造成缓存的污染,让Cache中其他有用的数据提前被替换。
总结表
不同点
|
顺序表
|
链表
|
存储空间上
|
物理上一定连续
|
逻辑上连续,但物理上不一定连续
|
随机访问
|
支持
O(1)
|
不支持:
O(N)
|
任意位置插入或者删除元素
|
可能需要搬移元素,效率低
O(N)
|
只需修改指针指向
|
插入
|
动态顺序表,空间不够时需要扩容
|
没有容量的概念
|
应用场景
|
元素高效存储
+
频繁访问
|
任意位置插入和删除频繁
|
缓存利用率
| 高 | 低 |