前言
上一篇博客介绍了线性表中的顺序表—它存储的元素在内存中是连续的。但是内存中的数据可以是不连续的,顺序表是处理不了这种情形,因此链表—将内存中不联系的独立的元素链接起来。
链表
什么是链表
概念:链表是一种
物理存储结构上非连续
、非顺序的存储结构,数据元素的逻辑顺序
是通过链表中next指针
链接次序实现的由此可见链表是一个
结构体
。另外链表只是将
独立
的结点链接起来,因此对于那些独立的结点我们可以操作很多事。
链表的分类
链表中
按单向或者双向,是否带哨兵结点,是否成环,
将链表分为8种,但是oj题目基本只以2种链表来考察——单向链表,双向带头成环链表,这2种链表在功能和结构上是互补的,下面会分析
单向链表
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType date;
struct SListNode* next;
}SLTNode;
从图中可以看出单链表:
不再需要顺序表那样,必须要存储的元素在内存是连续的。
但是
- 它不能像数组通过
下标
那样,可以直接访问存储的元素,- 另外链表去访问元素只能通过next指针一个一个去遍历访问。
- 另外我们想访问结点前一个元素非常麻烦,只能遍历,因此链表又衍生出—-双向链表,在结构体中设置一个记录
prev指针
,指向前一个结点。
单链表操作代码实现
双向链表
typedef int DLDateType;
typedef
struct DList
{
struct DList* prev;
DLDateType date;
struct DList* next;
}DList;
从图中可以看出
每个结点前一个结点很容易就可以得到,但是对于头结点它的prev指向不好处理。
因此双向链表通常于
哨兵结点
一起使用。下面介绍
带头双向链表
。
哨兵结点
哨兵结点是一个不存储任何数据的结点,设置它的目地是为了解决某些链表结构不好的问题。比如
单链表要时刻注意头结点是否为空的问题
,双向链表头结点的prev指向
等问题
带头双向链表
从图中可以看出,有了哨兵结点,我们就不需要考虑链表为空和双向头结点prev指向问题。
带头双向循环链表
可以看出带头双向循环链表的结构非常复杂,但是我们对它增删减查非常方便。
这与单链表刚好相反,因此我们说单链表和带头双向循环链表是互补的,绝大多数题目都以这2种类型考察。
带头双向循环链表功能复现
总结
理解链表的关键是理解指针。
多画图,会极大的帮助解决问题