链表 (LinkList
)
1. 链表的特点
2. 实现链表
-
条件设定:
- 链表的节点都应通过
Node
类实例化得到 - 链表实例化时,自动生成头指针,
new Node('head')
, 其next
属性默认为null
- 头指针点不计入链表的总长度
- 链表最后一个节点的
next
属性永远指向null
- 链表的节点都应通过
-
代码
// 节点类
// 每个节点包含节点的值和节点下一个节点的引用(`next`属性)
// 刚初始化的节点默认指向 null
class Node {
constructor(value) {
this.value = value
this.next = null
}
}
/**
* 链表类
*/
class LinkedList {
constructor() {
this.head = new Node('head')
this.length = 0
}
// 向链表的尾部插入一个元素
append(...values) {
values.forEach(v => {
const tailNode = this.findTailNode()
tailNode.next = new Node(v)
this.length++
})
}
// 查找链表末尾元素
findTailNode() {
let tail = this.head
while (tail.next) {
tail = tail.next
}
return tail
}
// 在某个节点后插入一个节点
// 在值为 value 的节点后边新添加一个元素 newValue
insertAfter(newValue, value) {
const newNode = new Node(newValue);
const nodeBefore = this.findByValue(value)
newNode.next = nodeBefore.next
nodeBefore.next = newNode
this.length++
}
// 在某个节点前插入一个节点
// 在值为 value 的节点前边边新添加一个元素 newValue
insertBefore(newValue, value) {
const prevNode = this.findPreviosByValue(value)
const currentNode = this.findByValue(value)
const newNode = new Node(newValue)
newNode.next = currentNode
prevNode.next = newNode
this.length++
}
// 通过值去查找节点,
// 查找到了, 返回元素
// 找不到的话返回 null
findByValue(value) {
let currentNode = this.head
while (currentNode && currentNode.value !== value) {
currentNode = currentNode.next
}
return currentNode
}
// 通过值删除链表节点
removeByValue(value) {
const prevNode = this.findPreviosByValue(value)
let currentNode = this.findByValue(value)
if (!prevNode || !currentNode) return false;
prevNode.next = currentNode.next
currentNode = null
this.length--
return true
}
// // 通过值查找节点索引
indexOf(value) {
// 头结点不计入索引, 从头结点后第一个节点开始记录
let currentNode = this.head.next,
i = 0
for (; i < this.length; i++) {
if (currentNode.value === value) {
return i
} else {
currentNode = currentNode.next
}
}
return -1
}
// 是否有包含某个值的节点
includes(value) {
return this.indexOf(value) > -1
}
// 通过索引查找节点
findByIndex(index) {
const isOutBoundary = index < -1 || index >= this.length
if (isOutBoundary) return null
let currentNode = this.head.next
for (let i = 0; i < this.length; i++) {
if (index === i) {
return currentNode
} else {
currentNode = currentNode.next
}
}
}
// 查找当前索引的上一个元素
findPreviosByIndex(index) {
const isOutBoundary = index <= 0 || index > this.length
if (isOutBoundary) return null
let currentNode = this.head.next
for (let i = 0; i < this.length; i++) {
if (index === i + 1) {
return currentNode
} else {
currentNode = currentNode.next
}
}
return null
}
// 通过索引删除元素
removeByIndex(index) {
const isOutBoundary = index < 0 || index > this.length - 1
if (isOutBoundary) return null
const prevNode = this.findPreviosByIndex(index)
const currentNode = this.findByIndex(index)
prevNode.next = currentNode.next
this.length--
return currentNode
}
// 根据自定义条件删除
// 传入一个 fn(node) 返回 true 则删除
// fn(node) 返回 false 不删除
removeByCondition(fn) {
let currentNode = this.head.next
while (currentNode) {
if (fn.call(null, currentNode)) {
this.removeByValue(currentNode.value)
}
currentNode = currentNode.next
}
}
// 查找当前节点的上一个节点
// 删除当前节点的时候要将上个节点的 next 指向当前节点的 next 属性指向的节点
findPreviosByValue(value) {
let prevNode = this.head
while (prevNode.next && prevNode.next.value !== value) {
prevNode = prevNode.next
}
const isFound = prevNode.next && prevNode.next.value === value
return isFound ? prevNode : null
}
// 通过索引更新节点值
updateByIndex(index, newValue) {
const currentNode = this.findByIndex(index)
if (currentNode) {
currentNode.value = newValue
return true
}
return false
}
// 通过值更新节点值
updateByValue(oldValue, newValue) {
const currentNode = this.findByValue(oldValue)
if (currentNode) {
currentNode.value = newValue
return true
}
return false
}
// 打印链表到控制台
print() {
console.log(JSON.stringify(this, null, 2))
}
}
3. 双向链表
3.1 双向链表特点:
- 每个元素比但链表多了一个 prev 指针, 指向前一个节点
3.2 双向链表的实现
本来可以通过继承单链表, 只重写几个方法就可以了, 后来想了想, 以后使用双向链表还要引入单项链表, 所以就直接分离了。
// 双向链表节点,比普通链表多了 prev 属性
class DoublyNode {
constructor(value) {
this.prev = null
this.next = null
this.value = value
}
}
// 双向链表节点,比普通链表多了 prev 属性
class DoublyLinkedList {
constructor() {
super()
this.head = new DoublyNode(`head`)
this.length = 0
}
// // 通过值查找节点索引
indexOf(value) {
// 头结点不计入索引, 从头结点后第一个节点开始记录
let currentNode = this.head.next,
i = 0
for (; i < this.length; i++) {
if (currentNode.value === value) {
return i
} else {
currentNode = currentNode.next
}
}
return -1
}
// 是否有包含某个值的节点
includes(value) {
return this.indexOf(value) > -1
}
// 通过索引查找节点
findByIndex(index) {
const isOutBoundary = index < -1 || index >= this.length
if (isOutBoundary) return null
let currentNode = this.head.next
for (let i = 0; i < this.length; i++) {
if (index === i) {
return currentNode
} else {
currentNode = currentNode.next
}
}
}
// 查找当前索引的上一个元素
findPreviosByIndex(index) {
const isOutBoundary = index <= 0 || index > this.length
if (isOutBoundary) return null
let currentNode = this.head.next
for (let i = 0; i < this.length; i++) {
if (index === i + 1) {
return currentNode
} else {
currentNode = currentNode.next
}
}
return null
}
// 查找链表末尾元素
findTailNode() {
let tail = this.head
while (tail.next) {
tail = tail.next
}
return tail
}
// 通过值去查找节点,
// 查找到了, 返回元素
// 找不到的话返回 null
findByValue(value) {
let currentNode = this.head
while (currentNode && currentNode.value !== value) {
currentNode = currentNode.next
}
return currentNode
}
// 越界检查
isOutBoundary(index) {
return index < 0 || index >= this.length
}
// 在当前索引添加一个元素
insert(index, value) {
if (this.isOutBoundary(index)) return false;
const node = new DoublyNode(value)
const currentNode = this.findByIndex(index)
// 在第一个位置增加
if (index === 0) {
node.next = currentNode
currentNode.prev = node
this.head.next = node
node.prev = this.head
} else {
node.prev = currentNode.prev
node.next = currentNode
currentNode.prev.next = node
}
this.length++
return true
}
// 以下方法需要全部重写,属于对链表的增删改, 链表的查询方法不需要重写
// OK 向链表尾部增加元素
append(...values) {
values.forEach(v => {
const tailNode = this.findTailNode()
const node = new DoublyNode(v)
tailNode.next = node
node.prev = tailNode
this.length++
})
}
// 在某个节点后插入一个节点
// OK 在值为 value 的节点后边新添加一个元素 newValue
insertAfter(value, newValue) {
const newNode = new Node(newValue);
const currentNode = this.findByValue(value)
newNode.next = currentNode.next
currentNode.next.prev = newNode
newNode.prev = currentNode
currentNode.next = newNode
this.length++
}
// 在某个节点前插入一个节点
// ok 在值为 value 的节点前边边新添加一个元素 newValue
insertBefore(value, newValue) {
const currentNode = this.findByValue(value)
const newNode = new Node(newValue)
newNode.next = currentNode
newNode.prev = currentNode.prev
currentNode.prev.next = newNode
currentNode.prev = newNode
this.length++
}
// OK 通过值删除链表节点
removeByValue(value) {
const prevNode = this.findPreviosByValue(value)
let currentNode = this.findByValue(value)
if (!prevNode || !currentNode) return false;
prevNode.next = currentNode.next
currentNode.next.prev = prevNode
this.length--
return true
}
// OK 通过索引删除元素
removeByIndex(index) {
const isOutBoundary = index < 0 || index > this.length - 1
if (isOutBoundary) return null
const prevNode = this.findPreviosByIndex(index)
const currentNode = this.findByIndex(index)
prevNode.next = currentNode.next
currentNode.next.prev = prevNode
this.length--
return currentNode
}
// 查找当前节点的上一个节点
// 删除当前节点的时候要将上个节点的 next 指向当前节点的 next 属性指向的节点
findPreviosByValue(value) {
let prevNode = this.head
while (prevNode.next && prevNode.next.value !== value) {
prevNode = prevNode.next
}
const isFound = prevNode.next && prev.next.value === value
return isFound ? prevNode : null
}
// 通过索引更新节点值
updateByIndex(index, newValue) {
const currentNode = this.findByIndex(index)
if (currentNode) {
currentNode.value = newValue
return true
}
return false
}
// 通过值更新节点值
updateByValue(oldValue, newValue) {
const currentNode = this.findByValue(oldValue)
if (currentNode) {
currentNode.value = newValue
return true
}
return false
}
}
4. 循环链表
5. API
1. 属性
2. 方法
接口名称 | 说明 | 参数 |
---|---|---|
append | 向链表插入节点, 节点的值为 value | (value : any) |
单元格 | 单元格 | 单元格 |