双向链表
在计算机科学中, 一个 双向链表(doubly linked list) 是由一组称为节点的顺序链接记录组成的链接数据结构。每个节点包含两个字段,称为链接,它们是对节点序列中上一个节点和下一个节点的引用。开始节点和结束节点的上一个链接和下一个链接分别指向某种终止节点,通常是前哨节点或 null,以方便遍历列表。如果只有一个前哨节点,则列表通过前哨节点循环链接。它可以被概念化为两个由相同数据项组成的单链表,但顺序相反。
两个节点链接允许在任一方向上遍历列表。
在双向链表中进行添加或者删除节点时,需做的链接更改要比单向链表复杂得多。这种操作在单向链表中更简单高效,因为不需要关注一个节点(除第一个和最后一个节点以外的节点)的两个链接,而只需要关注一个链接即可。

DoublyLinkedListNode 双向链表节点类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
class DoublyLinkedListNode {
constructor(value, prev = null, next = null) { this.value = value; this.prev = prev; this.next = next; }
toString(callback) { return callback ? callback(this.value) : `${this.value}`; } }
|
constructor
构造函数用于创建一个双向链表节点对象。它接受一个值作为参数,并可选地接受前一个节点和后一个节点的引用。构造函数将该值封装为节点的值,并将前一个节点和后一个节点的引用分别存储在 prev 和 next 属性中。
DoublyLinkedList 双向链表实现类
constructor(comparatorFunction) 方法
双向链表的构造函数.
它接受一个可选的 comparatorFunction
参数,用于比较链表中的节点。构造函数将 head
和 tail
属性初始化为 null
,并使用 comparatorFunction
创建一个 Comparator
对象。
1 2 3 4 5 6 7 8 9 10 11 12
|
constructor(comparatorFunction) { this.head = null;
this.tail = null;
this.compare = new Comparator(comparatorFunction); }
|
prepend(value) 方法
用于在链表的开头添加一个新节点.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
prepend(value) { const newNode = new DoublyLinkedListNode(value, this.head);
if (this.head) { this.head.previous = newNode; } this.head = newNode;
if (!this.tail) { this.tail = newNode; }
return this; }
|
append(value) 方法
将一个具有给定值的新节点添加到链表的末尾.
该方法首先创建一个新节点,节点的值为传入的 value
。然后,它检查链表是否为空。如果链表为空,将新节点设置为头结点和尾节点,然后返回当前链表实例。如果链表不为空,将新节点连接到链表的末尾,即将当前尾节点的 next 属性指向新节点,同时将新节点的 previous 属性指向当前尾节点。最后,将新节点设置为链表的尾节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
append(value) { const newNode = new DoublyLinkedListNode(value);
if (!this.head) { this.head = newNode; this.tail = newNode;
return this; }
this.tail.next = newNode;
newNode.previous = this.tail;
this.tail = newNode;
return this; }
|
delete(value) 方法
该方法通过遍历链表来查找具有指定值的节点。如果找到了该节点,则通过调整前驱节点和后继节点的指针来删除节点。如果被删除的节点是链表的头节点或尾节点,则相应地更新头指针或尾指针。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
|
delete(value) { if (!this.head) { return null; }
let deletedNode = null; let currentNode = this.head;
while (currentNode) { if (this.compare.equal(currentNode.value, value)) { deletedNode = currentNode;
if (deletedNode === this.head) { this.head = deletedNode.next;
if (this.head) { this.head.previous = null; }
if (deletedNode === this.tail) { this.tail = null; } } else if (deletedNode === this.tail) {
this.tail = deletedNode.previous; this.tail.next = null; } else {
const previousNode = deletedNode.previous; const nextNode = deletedNode.next;
previousNode.next = nextNode; nextNode.previous = previousNode; } }
currentNode = currentNode.next; }
return deletedNode; }
|
find({ value = undefined, callback = undefined }) 方法
在双向链表中查找节点.
它接收一个名为 findParams
的对象作为参数,该对象可以有两个属性:value
和 callback
。方法通过遍历链表的节点,并检查回调函数是否对节点的值返回 true
,或者检查节点的值是否与 value
属性相等(使用 compare.equal
方法)。如果找到匹配的节点,则返回该节点;否则返回 null
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
find({ value = undefined, callback = undefined }) { if (!this.head) { return null; }
let currentNode = this.head;
while (currentNode) { if (callback && callback(currentNode.value)) { return currentNode; }
if (value !== undefined && this.compare.equal(currentNode.value, value)) { return currentNode; }
currentNode = currentNode.next; }
return null; }
|
deleteTail() 方法
删除尾节点.
- 首先通过检查
this.tail
是否为 null
来判断链表是否为空。如果为空,表示没有尾部节点可以删除,所以返回 null
。
- 如果链表只有一个节点,即
this.head
等于 this.tail
,则将要删除的尾部节点保存到 deletedTail
变量中。将 this.head
和 this.tail
都置为 null
,表示链表为空。然后返回被删除的尾部节点。
- 如果链表中有多个节点,则将要删除的尾部节点保存到
deletedTail
变量中。
- 将
this.tail
更新为前一个节点,即将尾部节点指向前一个节点。
- 将新的尾部节点的
next
指针置为 null
,表示它是链表的最后一个节点。
- 然后返回被删除的尾部节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
deleteTail() { if (!this.tail) { return null; }
if (this.head === this.tail) { const deletedTail = this.tail; this.head = null; this.tail = null;
return deletedTail; }
const deletedTail = this.tail;
this.tail = this.tail.previous; this.tail.next = null;
return deletedTail; }
|
deleteHead() 方法
用于删除链表的头节点。
如果链表为空,则返回 null
。否则,它会将头节点保存到 deletedHead
变量中。如果头节点有下一个节点,它会将头节点更新为下一个节点,并将新的头节点的 previous
指针设为 null
,断开与之前头节点的连接。如果头节点没有下一个节点,说明链表只有一个节点,那么它会将头节点和尾节点都设为 null
,表示链表为空。最后,它会返回被删除的头节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
deleteHead() { if (!this.head) { return null; }
const deletedHead = this.head;
if (this.head.next) { this.head = this.head.next; this.head.previous = null; } else { this.head = null; this.tail = null; }
return deletedHead; }
|
toArray() 方法
链表转数组的方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
toArray() { const nodes = [];
let currentNode = this.head;
while (currentNode) { nodes.push(currentNode);
currentNode = currentNode.next; }
return nodes; }
|
fromArray(values) 方法
数组转双向链表.
1 2 3 4 5 6 7 8 9 10 11
|
fromArray(values) { values.forEach((value) => this.append(value));
return this; }
|
toString(callback) 方法
1 2 3 4 5 6 7 8
|
toString(callback) { return this.toArray().map((node) => node.toString(callback)).toString(); }
|
reverse() 方法
反转一个双向链表.
它通过交换每个节点的 next
和 previous
指针来实现链表的反转。方法开始时,将三个变量进行初始化:currNode
设置为链表的头部,prevNode
设置为 null
,nextNode
设置为 null
。然后,进入一个循环,遍历链表中的每个节点。在循环内部,交换当前节点的 next
和 previous
指针,并更新 prevNode
和 currNode
变量。循环结束后,更新链表的 head
和 tail
指针以反映反转后的顺序,并返回修改后的链表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
reverse() { let currNode = this.head; let prevNode = null; let nextNode = null;
while (currNode) { nextNode = currNode.next; prevNode = currNode.previous;
currNode.next = prevNode; currNode.previous = nextNode;
prevNode = currNode; currNode = nextNode; }
this.tail = this.head; this.head = prevNode;
return this; }
|