链表是一种不需要连续空间存储的线性数据结构,通过指针把数据节点链接起来。数据节点的逻辑顺序取决于链表指针链接的先后顺序,同时,链表也有:单链表、双链表、循环链表等多类。
定义
链表是由一系列节点组成,除此之外链表还有几个基本概念:
- 数据节点
一般由两块组成:数据域、指针域。
- 首元节点
链表的第一个数据节点。
- 头节点
非必要节点,放在第一个数据节点前。一般用于存储链表的信息(比如说链表长度等)和保证数据节点操作的统一性。
- 头指针
指向第一个节点的指针,如果有头节点则指向头节点,否则指向首元节点。
单链表
最简单的一种链表,只支持一个遍历方向:链头 -> 链尾。两部分组成:当前节点的数据、指向后置节点的指针(尾节点指针指向nil)。
例子:
1 | type Node struct { |
以上是单链表的简单实现,其中添加节点的操作也被称为:尾插法
,封装一下:
1 | type Node struct { |
如果有数据节点想要插队,怎么办?头插法
,思路就是:把头节点作为新增节点的后置节点,新增节点作为新的头节点。
1 | func (l *List) Insert(node *Node) { |
还想把链表反转,数据节点逆序呢?有很多种方式,比如:利用双指针迭代、递归等等
迭代,思路和头插法一致。
1 | func (l *List) Reverse() { |
删除操作:
1 | func (l *List) Delete(node *Node) { |
单链表的实现比较简单,可以很轻松的查询到指定节点的后置节点,但如果想要查询前置节点则需要遍历一遍才可以,所以这就需要双链表
了
双链表
双链表通俗的讲,就是每个数据节点既可以找到它前一个节点也找的到它后一个节点。三部分组成:前值节点的指针,节点数据,后置节点的指针(尾节点指针指向nil)。
直接上代码:
1 | type Node struct { |
双链表较单链表的不同比较明显,双链表可以很轻松的查找到指定节点的前置和后置节点,遍历方向也可以是从头到尾或者从尾到头。同时,双链表节点操作比较复杂,占用空间也更大。
循环链表
顾名思义,循环链表是一个环状的链表。循环链表又分为:单向循环链表、双向循环链表,实现上就是把尾节点和头节点进行链接。
单向循环链表:
1 | type Node struct { |
使用场景
链表是最基础且很常用的一种数据结构,可以解决很多算法问题,也可以用于构建其他的数据结构。比如说解决约瑟夫环问题
和实现堆栈、队列等。