前端说的栈、队列,它们都是列表的一种,底层的数据结构都是数组。
但是数组并不总是最佳的数据结构,索引引入链表结构:
链表(LinkedList)
介绍
链表的出现是为了解决数组增删的操作,在js中,虽然存在如下方法:
- 尾部添加
push
- 尾部删除
pop
- 头部添加
unshift
- 头部删除
shift
- 任意位置添加删除
splice
但是与其他语言相比,效率会比较低,所以考虑使用链表(Linked-list
)来替换它,链表几乎可以在任何可以使用一位数组的情况中。
链表包含:
- 单向链表
- 双向链表
- 单向循环列表
- 双向循环列表
定义
链表是一组节点组成的集合,每个节点都使用一个对象的引用来指向它的后一个节点。指向另一个节点的引用称作为链。 链表结构图
其中,data保存数据,next保存着下一个链表的引用,链表的尾元素指向null,表示结束位置。
由于链表的起始点确定比较麻烦,因此在链表的最前面添加一个特殊的节点,成为头节点,表示链表的头部,如: 有头节点的链表
用我们常见的数据结构,大致是这样: 链表数据结构
单向链表
设计链表包含两个类,一个是 Node
类用来表示节点,另一个是 LinkList
类提供增删等操作。
Node类
1
2
3
4function Node(el) {
this.el = el // 当前节点元素
this.next = null // 下一个节点的链接
}LinkList类
1
2
3
4
5
6
7
8
9function LinkList() {
this.head = new Node('head') // 头结点
this.find = find // 查找结点
this.findPrev = findPrev // 查找前一个结点
this.insertAfter = insertAfter // 在某个节点后插入节点(结合find)
this.insertBefore = insertBefore // 在某个节点后插入节点(结合findPrev)
this.remove = remove // 删除结点(结合findPrev)
this.display = display // 显示链表
}
下面挨个实现这些方法:
find 查找节点
1 | function find(el) { |
findPrev 查找前一个结点
1 | function findPrev(el) { |
insertAfter 在某个节点后插入节点
1 | function insertAfter(newEl, el) { |
insertBefore 在某个节点前插入节点
1 | function insertBefore(newEl, el) { |
remove 删除节点
1 | function remove(el) { |
display 显示链表
1 | function display() { |
全部代码如下:
1 | function Node(el) { |
测试:
1 | var nums = new LinkList() |
双向链表
要实现双向链表,除了 next
外,还需要一个 previous
属性:
1 | function Node(el) { |
完整代码如下:
1 | function Node(el) { |
单向循环链表
单向循环链表,尾结点的next指向头节点,即: 单向循环链表结构图
循环链表判断最后一个节点的条件不再是“后继是否为空”,而是“后继是否为头节点”。
双向循环链表
单向循环列表链表 + 双向链表 组合情况