TypeScript方式实现源码
// 双向链表和普通链表的区别在于, 在链表中, // 一个节点只有链向下一个节点的链接,而在双向链表中,链接是双向的:一个链向下一个元素, // 另一个链向前一个元素,如下图所示: // // Node类里有prev属性(一个新指针) ,在DoublyLinkedList类里也有用来保存对列表最后一 // 项的引用的tail属性。 // 双向链表提供了两种迭代列表的方法:从头到尾,或者反过来。我们也可以访问一个特定节 // 点的下一个或前一个元素。在单向链表中,如果迭代列表时错过了要找的元素,就需要回到列表 // 起点,重新开始迭代。这是双向链表的一个优点。
// 代码角度
// 每个节点设置两个指针,关联相邻的前后节点.提供逆向遍历,链表篇提到,链表是针对大量删除与添加场景下使用的.因此双向链表是针对普通链表的功能为基础,强化了遍历的功能
// 抽象
// 想了很久找不到现实中的场景,还是拿火车来理解吧.早期火车就像普通链表,具有重组车厢的功能.而随着科技的进步,双向列车出来了,优势便是不用再每次脱离车头,开到转盘进行掉头
// 然后再行驶.双向链表便是双向列车,它弱化了车头的概念,前后皆可是车头.
// 总结
// 单向链表优势在于可执行大量删除、增加操作并不用像数组那样需要重新排序.劣势则是每次便利必须从头开始,无端增加许多系统开销.假如有这种场景,知道某个节点,我需要找到
// 该节点的爷爷,再找他爷爷的儿子,再找他儿子的孙子.我们大脑瞬间可以计算出,这不就是要找当前节点的儿子吗?的确!我们就是要程序能够这样做,但是单向链表就是一根筋,不具备
// 这样的智商,这时双向链表优势则非常明显
1 class Node { 2 element; 3 next; 4 prev; // 新增的 5 constructor(element) { 6 this.element = element; 7 this.next = null; 8 this.prev = null; 9 } 10 } 11 12 /** 13 * 双向链表 14 * @name DoublyLinkedList 15 */ 16 class DoublyLinkedList { 17 private length = 0; 18 head = null; 19 tail = null; // 新增的 20 21 /** 22 * 向列表尾部添加一个新的项 23 * @param element 24 */ 25 public append(element) { 26 let node = new Node(element), current; 27 28 if (this.head === null) { 29 this.head = node; 30 } else { 31 current = this.head; 32 33 // 循环列表,知道找到最后一项 34 while (current.next) { 35 current = current.next; 36 } 37 38 // 找到最后一项,将其next赋为node,建立链接 39 current.next = node; 40 } 41 this.length++; // 更新列表长度 42 } 43 /** 44 * 向列表的特定位置插入一个新的项 45 * @param position 46 * @param element 47 */ 48 public insert(position, element) { 49 // 检查越界值 50 if (position >= 0 && position <= this.length) { 51 let node = new Node(element), 52 current = this.head, 53 previous, 54 index = 0; 55 56 if (position === 0) { // 在第一个位置添加 57 if (!this.head) { // 新增的 58 this.head = node; 59 this.tail = node; 60 } else { 61 node.next = current; 62 current.prev = node; // 新增的 63 this.head = node; 64 } 65 } else if (position === this.length) { // 最后一项 新增的 66 current = this.tail; 67 current.next = node; 68 node.prev = current; 69 this.tail = node; 70 } else { 71 while (index++ < position) { 72 previous = current; 73 current = current.next; 74 } 75 node.next = current; 76 previous.next = node; 77 78 current.prev = node; // 新增的 79 node.prev = previous; // 新增的 80 } 81 this.length++; // 更新列表长度 82 return true; 83 } else { 84 return false; 85 } 86 } 87 /** 88 * 从列表的特定位置移除一项 89 * @param position 90 */ 91 public removeAt(position) { 92 // 检查越界值 93 if (position > -1 && position < this.length) { 94 let current = this.head, 95 previous, 96 index = 0; 97 98 // 移除第一项 99 if (position === 0) { 100 this.head = current.next; 101 102 // 如果只有一项,更新tail // 新增的 103 if (this.length === 1) { 104 this.tail = null; 105 } else { 106 this.head.prev = null; 107 } 108 } 109 else if (position === this.length - 1) { // 新增的 110 current = this.tail; 111 this.tail = current.prev; 112 this.tail.next = null; 113 } 114 else { 115 while (index++ < position) { 116 previous = current; 117 current = current.next; 118 } 119 // 将previous与current的下一项链接起来:跳过current,从而移除它 120 previous.next = current.next; 121 current.next.prev = previous; // 新增的 122 } 123 this.length--; 124 return current.element; 125 } else { 126 return null; 127 } 128 } 129 /** 130 * 从列表中移除一项 131 * @param element 132 */ 133 public remove(element) { 134 let index = this.indexOf(element); 135 return this.removeAt(index); 136 } 137 /** 138 * :返回元素在列表中的索引。如果列表中没有该元素则返回-1 139 * @param element 140 */ 141 public indexOf(element) { 142 let current = this.head, 143 index = -1; 144 145 while (current) { 146 if (element === current.element) { 147 return index; 148 } 149 index++; 150 current = current.next; 151 } 152 return -1; 153 } 154 /** 155 * 如果链表中不包含任何元素, 返回true, 如果链表长度大于0则返回false 156 */ 157 public isEmpty() { 158 return this.length === 0; 159 } 160 /** 161 * 返回链表包含的元素个数。与数组的length属性类似 162 */ 163 public size() { 164 return this.length; 165 } 166 /** 167 * 由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值 168 */ 169 public toString() { 170 let current = this.head, 171 string = ''; 172 173 while (current) { 174 string += current.element; 175 current = current.next; 176 } 177 return string; 178 } 179 public getHead() { 180 return this.head; 181 } 182 public print() { 183 console.log(this.toString()); 184 } 185 }
JavaScript 方式实现源码
1 var Node = (function () { 2 function Node(element) { 3 this.element = element; 4 this.next = null; 5 this.prev = null; 6 } 7 return Node; 8 }()); 9 /** 10 * 双向链表 11 * @name DoublyLinkedList 12 */ 13 var DoublyLinkedList = (function () { 14 function DoublyLinkedList() { 15 this.length = 0; 16 this.head = null; 17 this.tail = null; // 新增的 18 } 19 /** 20 * 向列表尾部添加一个新的项 21 * @param element 22 */ 23 DoublyLinkedList.prototype.append = function (element) { 24 var node = new Node(element), current; 25 if (this.head === null) { 26 this.head = node; 27 } 28 else { 29 current = this.head; 30 // 循环列表,知道找到最后一项 31 while (current.next) { 32 current = current.next; 33 } 34 // 找到最后一项,将其next赋为node,建立链接 35 current.next = node; 36 } 37 this.length++; // 更新列表长度 38 }; 39 /** 40 * 向列表的特定位置插入一个新的项 41 * @param position 42 * @param element 43 */ 44 DoublyLinkedList.prototype.insert = function (position, element) { 45 // 检查越界值 46 if (position >= 0 && position <= this.length) { 47 var node = new Node(element), current = this.head, previous = void 0, index = 0; 48 if (position === 0) { 49 if (!this.head) { 50 this.head = node; 51 this.tail = node; 52 } 53 else { 54 node.next = current; 55 current.prev = node; // 新增的 56 this.head = node; 57 } 58 } 59 else if (position === this.length) { 60 current = this.tail; 61 current.next = node; 62 node.prev = current; 63 this.tail = node; 64 } 65 else { 66 while (index++ < position) { 67 previous = current; 68 current = current.next; 69 } 70 node.next = current; 71 previous.next = node; 72 current.prev = node; // 新增的 73 node.prev = previous; // 新增的 74 } 75 this.length++; // 更新列表长度 76 return true; 77 } 78 else { 79 return false; 80 } 81 }; 82 /** 83 * 从列表的特定位置移除一项 84 * @param position 85 */ 86 DoublyLinkedList.prototype.removeAt = function (position) { 87 // 检查越界值 88 if (position > -1 && position < this.length) { 89 var current = this.head, previous = void 0, index = 0; 90 // 移除第一项 91 if (position === 0) { 92 this.head = current.next; 93 // 如果只有一项,更新tail // 新增的 94 if (this.length === 1) { 95 this.tail = null; 96 } 97 else { 98 this.head.prev = null; 99 } 100 } 101 else if (position === this.length - 1) { 102 current = this.tail; 103 this.tail = current.prev; 104 this.tail.next = null; 105 } 106 else { 107 while (index++ < position) { 108 previous = current; 109 current = current.next; 110 } 111 // 将previous与current的下一项链接起来:跳过current,从而移除它 112 previous.next = current.next; 113 current.next.prev = previous; // 新增的 114 } 115 this.length--; 116 return current.element; 117 } 118 else { 119 return null; 120 } 121 }; 122 /** 123 * 从列表中移除一项 124 * @param element 125 */ 126 DoublyLinkedList.prototype.remove = function (element) { 127 var index = this.indexOf(element); 128 return this.removeAt(index); 129 }; 130 /** 131 * :返回元素在列表中的索引。如果列表中没有该元素则返回-1 132 * @param element 133 */ 134 DoublyLinkedList.prototype.indexOf = function (element) { 135 var current = this.head, index = -1; 136 while (current) { 137 if (element === current.element) { 138 return index; 139 } 140 index++; 141 current = current.next; 142 } 143 return -1; 144 }; 145 /** 146 * 如果链表中不包含任何元素, 返回true, 如果链表长度大于0则返回false 147 */ 148 DoublyLinkedList.prototype.isEmpty = function () { 149 return this.length === 0; 150 }; 151 /** 152 * 返回链表包含的元素个数。与数组的length属性类似 153 */ 154 DoublyLinkedList.prototype.size = function () { 155 return this.length; 156 }; 157 /** 158 * 由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值 159 */ 160 DoublyLinkedList.prototype.toString = function () { 161 var current = this.head, string = ''; 162 while (current) { 163 string += current.element; 164 current = current.next; 165 } 166 return string; 167 }; 168 DoublyLinkedList.prototype.getHead = function () { 169 return this.head; 170 }; 171 DoublyLinkedList.prototype.print = function () { 172 console.log(this.toString()); 173 }; 174 return DoublyLinkedList; 175 }());