在计算机科学中, 一个 双向链表(doubly linked list) 是由一组称为节点的顺序链接记录组成的链接数据结构。每个节点包含两个字段,称为链接,它们是对节点序列中上一个节点和下一个节点的引用。开始节点和结束节点的上一个链接和下一个链接分别指向某种终止节点,通常是前哨节点或null,以方便遍历列表。如果只有一个前哨节点,则列表通过前哨节点循环链接。它可以被概念化为两个由相同数据项组成的单链表,但顺序相反。
两个节点链接允许在任一方向上遍历列表。
在双向链表中进行添加或者删除节点时,需做的链接更改要比单向链表复杂得多。这种操作在单向链表中更简单高效,因为不需要关注一个节点(除第一个和最后一个节点以外的节点)的两个链接,而只需要关注一个链接即可。
基础操作的伪代码
插入
Add(value) Pre: value is the value to add to the list Post: value has been placed at the tail of the list n ← node(value) if head = ø head ← n tail ← n else n.previous ← tail tail.next ← n tail ← n end if end Add
下面是注释:
Add(value) Pre: value is the value to add to the list Post: value has been placed at the tail of the list //value是要被添加到双向链表中的值 //value会被添加到双向链表的尾部 n ← node(value)//value转换为节点对象赋值给n if head = ø//如果头结点是空节点,说明双向链表没有节点,那么头指针和尾指针都指向n head ← n tail ← n else//如果双向链表不是空链表,n的上一个节点指向尾节点,尾节点的下一个节点指向n,尾指针指向n n.previous ← tail tail.next ← n tail ← n end if end Add
删除
Remove(head, value) Pre: head is the head node in the list value is the value to remove from the list Post: value is removed from the list, true; otherwise false if head = ø return false end if if value = head.value if head = tail head ← ø tail ← ø else head ← head.Next head.previous ← ø end if return true end if n ← head.next while n = !ø and value != n.value n ← n.next end while if n = tail tail ← tail.previous tail.next ← ø return true else if n != ø n.previous.next ← n.next n.next.previous ← n.previous return true end if return false end Remove
下面是注释:
Remove(head, value) Pre: head is the head node in the list value is the value to remove from the list Post: value is removed from the list, true; otherwise false //head是双向链表的头结点,value是需要被删除的值 //value从双向链表中被删除,返回true,否则返回false if head = ø//如果头结点是空节点,返回false return false end if if value = head.value//如果头结点的值和value相等 if head = tail//如果头尾相等,说明只有一个节点,头尾指针都置空 head ← ø tail ← ø else//否则头指针指向头节点的下一个节点,新的头节点的前一个节点是空节点 head ← head.Next head.previous ← ø end if return true//删除头结点成功,返回true end if n ← head.next//如果不删除头结点,头结点的下一个节点赋值给n while n != ø and value != n.value//如果n不是空节点,并且n节点的值和value不相等就一直往后遍历 n ← n.next end while if n = tail//如果n是尾节点 tail ← tail.previous//尾指针指向尾节点的前一个节点 tail.next ← ø//新尾节点的下一个节点置空 return true//删除尾节点成功,返回true else if n != ø//如果n不是空节点 n.previous.next ← n.next//n的下一个节点赋值给n上一个节点的next指针 n.next.previous ← n.previous//n的上一个节点赋值给n的下一个节点的previous指针 return true//n节点删除成功,返回true end if return false//什么也没有删除,返回false end Remove
反向遍历
ReverseTraversal(tail) Pre: tail is the node of the list to traverse Post: the list has been traversed in reverse order n ← tail while n != ø yield n.value n ← n.previous end while end Reverse Traversal
下面是注释:
ReverseTraversal(tail) Pre: tail is the node of the list to traverse Post: the list has been traversed in reverse order //tail是反向遍历的起始位置,默认是尾节点 n ← tail//tail节点赋值给n while n != ø//如果n不是空节点,往前面遍历 yield n.value//生成当前节点的值 n ← n.previous//遍历到前一个节点 end while end Reverse Traversal
复杂度
时间复杂度
获取:O(n)
查询:O(n)
插入:O(1)
删除:O(1)
空间复杂度
O(n)
代码实现
DoublyLinkedList.js
import DoublyLinkedListNode from './DoublyLinkedListNode'; import Comparator from '../../utils/comparator/Comparator'; export default class DoublyLinkedList {//双向链表类 /** * @param {Function} [comparatorFunction] */ constructor(comparatorFunction) {//构造函数,comparatorFunction可以传递自定义的比较方法 /** @var DoublyLinkedListNode */ this.head = null;//双向链表的头指针 /** @var DoublyLinkedListNode */ this.tail = null;//双向链表的尾指针 this.compare = new Comparator(comparatorFunction); //compare属性,比较工具类Comparator对象,上面有各种比较方法 } /** * @param {*} value * @return {DoublyLinkedList} */ prepend(value) {//插入新节点作为头节点 // Make new node to be a head. const newNode = new DoublyLinkedListNode(value, this.head);//将value值转成双向链表节点对象 // If there is head, then it won't be head anymore. // Therefore, make its previous reference to be new node (new head). // Then mark the new node as head. if (this.head) {//如果原本有头结点,新头结点的previous指针指向新头结点 this.head.previous = newNode; } this.head = newNode;//双向链表的头指针指向新头结点 // If there is no tail yet let's make new node a tail. if (!this.tail) {//如果没有尾节点,说明目前为止只有一个节点,则尾指针指向新节点 this.tail = newNode; } return this;//返回当前双向链表对象 } /** * @param {*} value * @return {DoublyLinkedList} */ append(value) {//插入新节点作为尾节点 const newNode = new DoublyLinkedListNode(value);//将value值转成双向链表节点对象 // If there is no head yet let's make new node a head. if (!this.head) {//如果双向链表一个节点都没有,那么这个新节点既是头结点也是尾节点 this.head = newNode; this.tail = newNode; return this;//返回当前双向链表对象 } // Attach new node to the end of linked list. this.tail.next = newNode;//旧尾节点的next指针指向新节点 // Attach current tail to the new node's previous reference. newNode.previous = this.tail;//新节点的previous指针指向旧尾节点 // Set new node to be the tail of linked list. this.tail = newNode;//双向链表的尾指针指向新节点 return this;//返回当前双向链表对象 } /** * @param {*} value * @return {DoublyLinkedListNode} */ delete(value) {//删除节点 if (!this.head) {//如果没有节点,返回null return null; } let deletedNode = null;//即将被删除的节点 let currentNode = this.head;//遍历时用到的当前节点 while (currentNode) {//如果当前节点存在,就往后遍历 if (this.compare.equal(currentNode.value, value)) { //如果当前节点的值与value相等,说明当前节点需要被删除 deletedNode = currentNode; if (deletedNode === this.head) {//如果头结点需要被删除的情况 // If HEAD is going to be deleted... // Set head to second node, which will become new head. this.head = deletedNode.next;//头结点的下一个节点赋值给头指针 // Set new head's previous to null. if (this.head) {//如果新头节点存在,那么它的previous指针指向空节点 this.head.previous = null; } // If all the nodes in list has same value that is passed as argument // then all nodes will get deleted, therefore tail needs to be updated. if (deletedNode === this.tail) {//如果整个双向链表只有一个节点,那么尾指针也置空 this.tail = null; } } else if (deletedNode === this.tail) {//尾节点需要被删除的情况 // If TAIL is going to be deleted... // Set tail to second last node, which will become new tail. this.tail = deletedNode.previous;//尾指针指向当前尾节点的上一个节点 this.tail.next = null;//新尾节点的next指针指向空节点 } else {//中间的节点需要被删除的情况 // If MIDDLE node is going to be deleted... const previousNode = deletedNode.previous;//存下被删节点的前一个节点 const nextNode = deletedNode.next;//存下被删节点的下一个节点 previousNode.next = nextNode;//将被删节点的上一个节点和下一个节点连起来 nextNode.previous = previousNode; } } currentNode = currentNode.next;//继续往下一个节点遍历 } return deletedNode;//返回最后一个被删的节点 } /** * @param {Object} findParams * @param {*} findParams.value * @param {function} [findParams.callback] * @return {DoublyLinkedListNode} */ find({ value = undefined, callback = undefined }) {//查找value值对应的节点 if (!this.head) {//如果是空链表,返回null return null; } let currentNode = this.head;//遍历时用到的当前节点,从头节点开始 while (currentNode) {//当前节点存在,往后遍历 // If callback is specified then try to find node by callback. if (callback && callback(currentNode.value)) {//如果指定了callback,就用callback来寻找节点 return currentNode;//当前节点的值符合callback,返回当前节点 } // If value is specified then try to compare by value.. if (value !== undefined && this.compare.equal(currentNode.value, value)) { //如果没有callback就用默认的方法比较当前节点的值和value是否相等 return currentNode; } currentNode = currentNode.next;//继续遍历下一个节点 } return null; } /** * @return {DoublyLinkedListNode} */ deleteTail() {//删除尾节点 if (!this.tail) {//如果没有尾节点,返回null // No tail to delete. return null; } if (this.head === this.tail) {//如果链表只有一个节点,头尾指针都置空,然后返回这个节点 // There is only one node in linked list. const deletedTail = this.tail; this.head = null; this.tail = null; return deletedTail; } // If there are many nodes in linked list... const deletedTail = this.tail;//如果链表有多个节点 this.tail = this.tail.previous;//尾指针指向旧尾节点的上一个节点 this.tail.next = null;//新尾节点的next指针指向空节点 return deletedTail;//返回被删除的尾节点 } /** * @return {DoublyLinkedListNode} */ deleteHead() {//删除头节点 if (!this.head) {//如果没有头结点,返回null return null; } const deletedHead = this.head;//要被删除的节点 if (this.head.next) {//如果链表有多个节点 this.head = this.head.next;//头指针指向旧头结点的下一个节点 this.head.previous = null;//新头结点的previous指针指向空节点 } else {//如果链表只有一个节点,头尾节点都置空 this.head = null; this.tail = null; } return deletedHead;// 返回被删除的头结点 } /** * @return {DoublyLinkedListNode[]} */ toArray() {//将链表的值转换成一个数组 const nodes = [];//结果数组 let currentNode = this.head;//当前节点,从头结点开始 while (currentNode) {//遍历链表 nodes.push(currentNode);//结果数组push进当前节点 currentNode = currentNode.next;//当前节点移动到下一个 } return nodes;//返回数组 } /** * @param {*[]} values - Array of values that need to be converted to linked list. * @return {DoublyLinkedList} */ fromArray(values) {//插入数组元素作为节点 values.forEach(value => this.append(value));//循环数组,每一个元素变成链表节点插入到链表结尾 return this; } /** * @param {function} [callback] * @return {string} */ toString(callback) {//返回一个数组,每一个元素是当前节点调用节点的toString方法返回的值,支持自定义callback return this.toArray().map(node => node.toString(callback)).toString(); } /** * Reverse a linked list. * @returns {DoublyLinkedList} */ reverse() {//将链表顺序反转 let currNode = this.head;//当前节点,从头结点开始 let prevNode = null;//上一个节点 let nextNode = null;//下一个节点 while (currNode) {//遍历链表 // Store next node. nextNode = currNode.next;//存下一个节点 // Change next node of the current node so it would link to previous node. currNode.next = prevNode;//当前节点的next指针指向它的上一个节点 // Move prevNode and currNode nodes one step forward. prevNode = currNode;//当前节点存为上一个节点 currNode = nextNode;//当前节点存为下一个节点 } // Reset head and tail. //重置头尾指针 this.tail = this.head; this.head = prevNode; return this; } }
DoublyLinkedListNode.js
export default class DoublyLinkedListNode {//双向链表节点类 constructor(value, next = null, previous = null) { //构造函数,value节点值,next下一个节点指针,previous前一个节点指针 this.value = value; this.next = next; this.previous = previous; } toString(callback) {//返回节点的字符串类型值,支持回调函数处理 return callback ? callback(this.value) : `${this.value}`; } }