顺序表和链表其实是两个相辅相成,互补的数据结构,因为他们彼此之前都有自己个优势,也有自己的不足,这里我们来了解一下顺序表和链表的区别。这里的链表指的是带头双向循环链表
一.储存空间上
顺序表底层是数组实现的,而链表则是由一个一个的节点连接起来的,这些节点都是通过malloc动态申请的内存空间。他们的组成部分也就表明了两者在储存空间上的不同。
顺序表依靠数组实现,这就表明它在内存空间中一定是连续存储的。
而链表通过malloc申请的节点是随机申请的,它的内存地址可能连续也可能不连续。但是链表在逻辑上是连续的。
二.随机访问
顺序表借助数组实现,而数组中的元素都可以借助下标来快速访问指定的元素,所以顺序表可以实现元素的随机访问。
而对于链表来说,它的基本单位是一个一个的节点,而节点都是一个一个的结构体,里面包括数据域和指针域,数据域就是节点储存的数据,而指针域是指向前一个和后一个节点的指针。因为没有类似于数组下标的概念,所以链表做不到随机访问。
综上,那就是顺序表可以实现数据的随机访问,而链表不行!
三.任意位置上插入或删除元素
顺序表尾插还好,但是如果是头插或者在其他任意位置上插入数据的话,就得移动附近的所有数据。删除的话也一样,尾删直接删就行,但是要是删除尾部之外的数据的话,也会牵扯到数据的移动,很麻烦。
顺序表插入数据的时候还有一个问题:空间够不够?
因为顺序表由数组实现,而数组又是一段连续的储存空间,所以在创建顺序表的时候一般就会固定一个空间大小。当把这些空间使用完后,要是再想插入的话,就得通过ralloc来扩容。而realloc扩容的时候会有三种情况:
- 原地扩容:当扩容的时候,原地址后面的空间没有人用,而且够要扩容的大小,此时realloc就会在原地进行扩容,并且返回这段空间的起始地址。
- 异地扩容:当原空间后面的地址不够带扩容的大小或者后面的空间已经被使用了,此时realloc就会采取异地扩容的方式,先申请一个2倍的原空间大小,然后将原空间的数据拷贝到新地址,然后释放掉旧的空间,再返回新的地址。
- 扩容失败:扩容失败的情况那就是系统中没有足够的内存大小拿出来给ralloc的时候,就会扩容失败,返回NULL。
链表在插入和删除数据上就显得很方便了,只需要申请好节点,然后改变前后节点的指针指向就行了。
四.空间浪费
顺序表在空间不够的时候需要扩容,而且扩容时一般都是两倍的扩。比如我原本有100个元素的空间,现在要继续存储的话,直接就扩大到了200,而我只有101个元素,这是不是就造成了空间的浪费。
链表就不存在空间浪费的情况,用多少就申请多少节点就行了。
五.应用场景
顺序表的下标机制就反映出它适合做那些要频繁访问元素的项目,而链表在频繁插入删除数据这方面则更方便一些。
六.缓存利用率
顺序表的缓存利用率高,而链表的缓存利用率低,而且链表在缓存中还可能会造成内存污染。大家可以参考这篇文章与程序员相关的CPU缓存知识 | 酷 壳 - CoolShell