雙向鏈表也叫雙鏈表,是鏈表的一種,它的每一個數據節點中都有兩個指針,分別指向直接後繼和直接前驅。因此,雙向鏈表中的任意一個節點開始,均可以很方便的訪問它的前驅節點和後繼節點。javascript
linkednode.js ,裏面使用了類的繼承extends,使用了super函數。java
/** * 鏈表節點,鏈表中的項,鏈表中的節點 */ export class Node { constructor(element, next = null) { this.element = element // 鏈表中節點的值 this.next = next // 指向列表中下一個節點項的指針 } } export class DoublyNode extends Node { constructor(element, next = null, prev = null) { super(element, next) this.prev = prev } }
doublyLinkedList.js 雙向鏈表類,實現了各個功能,功能說明,都在代碼註釋中node
import { DoublyNode } from './linkednode' /** * 雙向鏈表類 */ export class DoublyLinkedList { constructor() { /** * 鏈表長度 */ this.length = 0 /** * 頭指針 */ this.head = null /** * 尾指針 */ this.tail = null } /** * 在鏈表末尾添加元素 * @param {*} element 須要插入的元素 */ append(element) { let node = new DoublyNode(element) if (!this.head) { this.head = node this.tail = node } else { this.tail.next = node node.prev = this.tail this.tail = node } this.length++ return true } /** * 在任意位置插入元素 * @param {Int32Array} position 指定位子 * @param {*} element 須要插入的元素 */ insert(position, element) { // 檢查越界值 if (position >= 0 && position <= this.length) { // 實例化一個雙向鏈表的節點 let node = new DoublyNode(element) // 賦初始值 let current = this.head let previous let index = 0 // 位置索引 if (position === 0) { // 在第一個位子添加 if (!this.head) { // 鏈表無數據的時候,將head和tail都指向新元素 this.head = node this.tail = node } else { // 鏈表有數據的時候, head node current node.next = current current.prev = node this.head = node } } else if (position === this.length) { // 添加到最後一項 current node current = this.tail current.next = node node.prev = current this.tail = node } else { // 在列表中間位置添加 // 新鏈表的節點原型是: previous <---> node <---> current while (index++ < position) { // 位置索引遞增到指定點以前,找出先後兩個節點 previous = current // 當前節點設置爲新鏈表中要插入的節點的前一個元素。 current = current.next // 當前節點以後的元素設置爲新鏈表中要插入的節點的當前元素 } node.next = current previous.next = node current.prev = node node.prev = previous } this.length++ // 更新列表的長度 return true } else { return false } } /** * 在任意位置插入元素 * 在鏈表頭,在鏈表尾,在鏈表前半段,在鏈表後半段 * @param {Int32Array} position 指定位置 * @param {*} element 須要插入的元素 */ insert_up(position, element) { let node = new DoublyNode(element) let previous let current = this.head if (position > -1 && position <= this.length) { if (position === 0) { if (!this.head) { this.head = node this.tail = node } else { node.next = current current.prev = node this.head = node } } else if (position === this.length) { current = this.tail current.next = node node.prev = current this.tail = node } else if (position < this.length / 2) { // 目標在鏈表前半段 let index = 0 // 0 1 2 [] 3 4 5 while (index++ < position) { previous = current current = current.next } previous.next = node node.next = current node.prev = previous current.prev = node } else { // 目標在鏈表的後半段 // 0 1 2 3 4 | 5 6 [] 7 8 9 let index = this.length current = this.tail while (index-- > position) { previous = current.prev current = current } previous.next = node node.next = current node.prev = previous current.prev = node } this.length++ return true } else { // 若是超出範圍,直接添加到鏈表末尾 let current = this.tail current.next = node node.prev = current this.tail = node this.length++ return true } } /** * 從任意位置移除元素,返回移除的元素 * 從頭部,從尾部,從鏈表的前半段,從鏈表的後半段 * @param {*} position 位置索引 */ removeAt(position) { let current = this.head // 當前項 let previous // 前一項 let index = 0 // 索引 // 越界檢查 if (position > -1 && position < this.length) { if (position === 0) { // 第一項 this.head = current.next // 若是是最後一項要刪除,將tail置爲null,此時head也爲null // 若是非最後一項,則將this.head.prev置爲null if (this.length === 1) { // 只有一項的狀況,更新tail this.tail = null } else { this.head.prev = null // 將首項的prev置空 或者 current.next.prev = null } } else if (position === this.length - 1) { // 最後一項 current = this.tail previous = current.prev this.tail = previous this.tail.next = null } else if (position <= this.length / 2) { // 索引在鏈表前半段,分開計算,提高性能 while (index++ < position) { previous = current current = current.next } // 將previous與current下一項連起來---跳過current previous.next = current.next current.next.prev = previous } else { // 索引在鏈表後半段 index = this.length - 1 current = this.tail while (index-- > position) { previous = current current = current.prev } // 將previous與current的上一項連起來--跳過current previous.prev = current.prev current.prev.next = previous } this.length-- return current.element } else { // 超出鏈表安全長度,鏈表有數據,則刪除末尾元素 if (typeof position === 'number' && this.length > 0) { let current = this.tail this.tail = current.prev this.tail.next = null this.length-- return current.element } else { return null } } } /** * 從列表中移除一項 * 先找出元素的索引項,再根據索引移除元素 * @param {*} element 列表中的元素 */ remove(element) { let index = this.indexOf(element) return this.removeAt(index) } /** * 返回元素在列表中的索引。若是列表中沒有該元素則返回-1 * @param {*} element 元素 */ indexOf(element) { let current = this.head let index = 0 // 計算位置數 while (current) { if (element === current.element) { return index } index++ current = current.next } return -1 } /** * 判斷是否爲空鏈表 * 空鏈表返回true,非空(鏈表長度大於0)返回false */ isEmpty() { return this.size() === 0 } /** * 返回鏈表包含的元素個數。與數組的length屬性相似 */ size() { return this.length } /** * 獲取鏈表的表頭節點 */ getHead() { return this.head } /** * 獲取鏈表的尾節點 */ getTail() { return this.tail } /** * 輸出元素的值 */ toString() { let current = this.head let string = 'null' while (current) { string += "<--->" + current.element + (current.next ? '' : '<--->null') current = current.next } return string } }
雙向鏈表與單項鍊表的比較:github
雙向鏈表能夠雙向遍歷。從頭至尾,或者從尾到頭數組
雙向鏈表能夠訪問一個特定節點的下一個或者前一個元素,而單鏈表只能訪問下一個元素。安全
雙向鏈表內存佔用比單鏈表的多app
鏈表還有一個雙向循環鏈表,在須要用到的時候,考慮它們各自的不一樣,選擇合適的鏈表來操做。框架