在計算機科學中, 一個 雙向鏈表(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}`; } }