JavaScript 数据结构与算法 — 双向链表
双向链表(Doubly Linked List)是链表类型中的一种,顾名思义,它的数据链接是双向的,它的链表节点有三部分组成:数据部分(value)、链向前一个节点的指针(prev)和链向下一个节点的指针(next)。
双向链表的结构如下图所示:

实现双向链表节点类
双向链表节点比单向链表节点多一个指针 prev,我们可以定义一个继承单向链表节点类的双向链表节点类:
/**
* DoublyNode.js
* @date: 2025-03-26
* @description: 创建双向链表节点的类
*/
import Node from './Node.js'
export default class DoublyNode extends Node {
constructor(value) {
super(value)
this.prev = null // 指向上一个节点的指针
}
}
实现双向链表类
双向链表也可以继承自单向链表的类,因为双向链表不仅可以从头部开始迭代,还可以从尾部开始迭代,所以双向链表类中最好维护一个对链表最后一个节点的引用 tail。
/**
* DoublyLinkedList.js
* @date: 2025-03-26
* @description: 双向链表
*/
import DoublyNode from '../../models/DoublyNode.js'
import LinkedList from './LinkedList.js'
export default class DoublyLinkedList extends LinkedList {
constructor() {
super()
// 双向链表提供了两种迭代方式,从头到尾和从尾到头,因此需要两个指针,一个是指向第一个节点的指针head(继承自单向链表),另一个是指向最后一个节点的指针tail
this.tail = null // 指向最后一个节点的指针
}
}
实现链表方法
双向链表中的方法与单向链表是一样的,操作链表时要格外注意 prev 指针的链接与断开即可。
insert 方法:任意位置插入元素
在 DoublyLinkedList 类中,我们将重写 insert 方法,因为链表是双向的,所以我们要考虑头部插入、尾部插入和其他位置插入这三种情况。具体代码展示如下:
// 在某个位置插入节点
insert(index, val) {
if (arguments.length < 2) return undefined
if (typeof arguments[0] !== 'number') return undefined
if (index < 0 || index > this.size) return undefined
const node = new DoublyNode(val)
let current = this.head
// 头部插入
if (index === 0) {
// 链表为空时,首尾指针都指向新添加的元素节点
if (!this.head) {
this.head = node
this.tail = node
} else {
node.next = this.head
current.prev = node
this.head = node
}
} else if (index === this.size) {
// 尾部插入
current = this.tail
current.next = node
node.prev = current
this.tail = node
} else {
// 获取插入点前后元素节点
const prev = this.getNodeAt(index - 1)
current = prev.next
// 插入新元素并与前后元素节点建立链接
prev.next = node
node.prev = prev
node.next = current
current.prev = node
}
this.size++
return this.size
}
上面代码中头部插入那部分代码我们可以通过 unshift 方法实现,尾部插入那部分代码我们可以通过 push 方法实现。这两部分提取出来的话为下面的代码:
// 向链表尾部添加元素节点
push(val) {
if (arguments.length === 0) return this.size
const node = new DoublyNode(val)
if (!this.head) {
this.head = node
this.tail = node
} else {
this.tail.next = node
node.prev = this.tail
this.tail = node
}
this.size++
return this.size
}
// 头部添加节点
unshift(val) {
if (arguments.length === 0) return this.size
const node = new DoublyNode(val)
if (!this.head) {
this.head = node
this.tail = node
} else {
this.head.prev = node
node.next = this.head
this.head = node
}
this.size++
return this.size
}
insert 最终代码可以表示为:
insert(index, val) {
if (arguments.length < 2) return undefined
if (typeof arguments[0] !== 'number') return undefined
if (index < 0 || index > this.size) return undefined
if (index === 0) return this.unshift(val)
if (index === this.size) return this.push(val)
const node = new DoublyNode(val)
const prev = this.getNodeAt(index - 1)
const next = prev.next
prev.next = node
node.prev = prev
node.next = next
next.prev = node
this.size++
return this.size
}
removeAt 方法:根据位置删除元素
跟 insert 插入方法一样,removeAt 方法也是分成三种情况:删除第一个节点、删除最后一个节点、删除其他位置节点。类似的,删除第一个节点的代码可以提取出来作为 shift方法,删除最后一个节点的代码可以提取出来作为 pop 方法。
// 头部删除节点
shift() {
if (this.isEmpty()) return undefined
const current = this.head
if (this.size === 1) {
this.head = null
this.tail = null
} else {
this.head = current.next
this.head.prev = null
}
this.size--
return current.value
}
// 尾部删除节点
pop() {
if (this.isEmpty()) return undefined
const current = this.tail
if (this.size === 1) {
this.head = null
this.tail = null
} else {
this.tail = current.prev
this.tail.next = null
}
this.size--
return current.value
}
// 删除某个位置的节点
removeAt(index) {
if (arguments.length === 0) return undefined
if (index < 0 || index >= this.size) return undefined
if (index === 0) return this.shift()
if (index === this.size - 1) return this.pop()
const current = this.getNodeAt(index)
current.prev.next = current.next
current.next.prev = current.prev
this.size--
return current.value
}
// 获取任意位置的节点
getNodeAt(index) {
if (arguments.length === 0) return undefined
if (index < 0 || index >= this.size) return undefined
let current = this.head
let i = 0
while (current) {
if (i === index) return current
current = current.next
i++
}
return undefined
}
remove 方法:删除元素
迭代链表,找到匹配的第一个元素值的位置,然后通过 removeAt 方法进行删除即可。
// 返回匹配值的第一个位置
indexOf(val) {
if (arguments.length === 0) return -1
if (this.isEmpty()) return -1
let current = this.head
let index = 0
while (current) {
if (this.options.isEqual(current.value, val)) return index
current = current.next
index++
}
return -1
}
// 删除某个值的节点
remove(val) {
if (arguments.length === 0) return undefined
const index = this.indexOf(val)
return this.removeAt(index)
}

浙公网安备 33010602011771号