线性数据结构——链表
链表是一个线性结构,同时也是一个天然的递归结构。链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
- js模拟链表
// 节点 (构成链表)
class Node {
constructor (v, next) {
this.value = v
this.next = next
}
}
// 链表的管理, 增删改查
class LinkList {
constructor () {
// 链表的长度
this.size = 0
// 初始化的值
this.initNode = new Node(null, null)
}
// 查询的是要删除或插入的下个节点
_find (header, index, currentIndex) {
// 递归查找
if (index === currentIndex) return header
return this._find(header.next, index, currentIndex)
}
// 检测index是否存在
_checkIndex (index) {
if (index < 0 || index > this.size) throw new Error('index错误');
}
/**
* @description:
* @param {type} v 节点的值
* @param {type} v index 插入节点的位置
* @return:
*/
insertNode (v, index) {
// 检测index是否存在
this._checkIndex(index)
// 查找当前的节点
// 传入一个初始化的空节点
let prev = this._find(this.initNode, index, 0)
// 当往链表的末尾插入的时候,prev.next为空
// 其他情况的时候,因为我们要插入节点,所以插入节点的next应该是prev.next
// 然后设置prev.next 为插入的节点
prev.next = new Node(v, prev.next)
this.size++
return prev.next
}
/**
* @description:
* @param {type} index 插入节点的位置
* @return:
*/
removeNode (index) {
// 检测index是否存在
this._checkIndex(index)
// 先找到需要删除的节点
let prev = this._find(this.initNode, index, 0)
// 当前需要操作的节点
let node = prev.next
prev.next = ndoe.next
ndoe.next = null
// size 减一
this.size--
return node
}
/**
* @description:
* @param {number} index 插入节点的位置
* @return:
*/
getNode(index) {
// 检测index是否存在
this._checkIndex(index)
if (this.isEmpty()) return
// 查找上一个节点next等价于当前节点
return this._find(this.initNode, index, index, 0).next
}
// 探空
isEmpty () {
return this.size === 0
}
// 获取链表的长度
getSize () {
return this.size
}
}
export default LinkList
- 链表的应用(力扣)
反转一个单链表
示例:
输入:1->2->3->4->5->null
输出:5->4->3-2->1->null
- 算法流程
- 创建两个变量,一个是未反转的链表指针,另一个是反转后的链表
- 递归遍历未反转的链表
- 修改next指针,并且当前链表指针向前遍历,直到next等于null
let reverseList = function(head) {
let cur = head
let prev = null
while(cur) {
const temp = cur.next
cur.next = prev
prev = cur
cur = temp
}
return prev
}
环形链表
- 环形链表(循环链表)是一种链式储存结构,它的最后一个节点指向头节点,形成了一个环
- 环形链表的特点是:从环形链表中的任何一个节点出发都能找到任何其他节点
双向链表
双向链表以类似的方式工作,但还有一个引用字段称为prev。有了这个额外的字段,就能够知道当前节点的前一个节点。
- 双向链表的特点:
- 比起单链表,双链表需要更多指针用于指向前驱节点,所以需要储存空间比单链表多一点
- 双链表需要同时维护next和prev两个指针
- 双链表中的元素访问需要通过顺序访问,即要通过遍历的方式来寻找元素