重新认识链表(上)

406 阅读4分钟

定义

链表(Linked list)是一种线性表数据结构。通常说链表的时候,会把它与数组放在一起进行比较,这样会更好的理解链表。链表克服了数组需要预先知道数据大小的缺点,并且它可以充分利用好内存空间,通过“指针”将一组零散的内存块串联起来使用,实现灵活的内存动态管理。但是链表失去了数组随机访问优点,同时链表由于增加了结点的指针域,空间开销比较大。

优缺点(以数组为参照,以单链表为例)

优点:

因为链表的存储空间不是连续的,所以不像数组呢样,每做一次插入删除操作的时候,为了保证内存数据的连续性,都需要做大量的数据迁移,也就意味着它的插入删除操作是十分快速的。通常只需要考虑相邻节点的指针的变化就可以了,所以时间复杂度很低,为O(1)。

缺点:

当然有利就有弊。链表的随机访问效率就不如数组了。同样原因,非连续存储,所以不能通过寻址公式来直接计算内存地址了。只能根据指针去一个节点一个节点的遍历,时间复杂度要搞一些,为O(n)。

常见的链表结构有:

单链表,双向链表和循环链表。

单链表

单链表是最简单的一种链表结构了,它包含两个域,一个信息域和一个指针域。并且是单向,结点只有一个后继指针 next 指向后面的结点。

Singly-linked-list.svg

双向链表

双向链表,顾名思义,它支持两个方向,一个前驱指针prev指向前面的节点,一个后继指针next指向后面的节点。同时它也比单链表更加占内存,但是它更灵活,可以双向遍历,所以对插入、删除操作更加简单、高效。

JAVA的LinkedHashMap容器底层就是这个双向链表。

Doubly-linked-list.svg

循环链表

循环链表,其实它是一种特殊的单链表。因为单链表的尾结点指针指向空地址,来表示结束。而循环链表的尾结点指针指向它的头结点。换一种角度想,循环链表也可以说是“无头无尾”,它比单链表要稍微省内存一些。

Circularly-linked-list.svg

问题:数组还是链表?链表还是数组?

数组:  

定义:是一段地址连续的存储单元依次存储数据元素的线性结构 

优点:存取速度高效,通过下标来直接存取 

缺点:插入和删除比较慢,不可以实时增长长度 

适用场景:适用于需要大量访问元素的,而增加/删除元素较少的程序 

链表:  

定义:一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称为存储单元为一个节点

优点:插入和删除速度快,保留原有的物理顺序 

缺点:查找元素需要遍历,因此不支持随机查找 

适用场景:适用于需要频繁进行增加/删除元素,而查找元素较少的程序

比较

在这里可以提一下一种设计思想:空间换时间。众所周知,当空间充足的时候,我们肯定会更加追求代码的执行效率,这个时候可以优先考虑链表。相反,如果内存不充足,比如单片机、手机等等,在这种内存即金钱的时候,则时间换空间,这个时候可以优先考虑数组。

总结:  数组查询效率高效,插入、删除操作效率低效,大小固定。链表查询效率低,插入、删除操作效率高效,没有大小限制。

扩展

下一篇则是对于链表具体问题具体分析。


end

您的点赞和关注是对我最大的支持,谢谢!