一、概念

单向链表
- 只能从头遍历到尾或者从尾遍历到头(一般从头到尾),也就是链表相连的过程是 单向 的。
- 实现的原理是上一个链表中有一个指向下一个的引用。
单链表的缺点:
- 我们可以轻松的到达下一个节点,但是回到前一个节点是很难的。 - 但是,在实际开发中,经常会遇到需要回到上一个节点的情况。 
- 举个例子: - 假设一个文本编辑用链表来存储文本。每一行用一个 String 对象存储在链表的一个节点中。 - 当编辑器用户向下移动光标时,链表直接操作到下一个节点即可。但是当用于将光标向上移动呢? - 这个时候为了回到上一个节点,我们可能需要从first开始,依次走到想要的节点上。 
双向链表
- 既可以从头遍历到尾,又可以从尾遍历到头。也就是链表的相连过程是双向的,
- 一个节点既有向前连接的引用,也有一个向后连接的引用。
优点:
- 每次在插入或删除某个节点时,需要处理四个引用,而不是两个。实现起来要困难一些。
- 相对于单向链表,占用的内存空间更大一些。
结构模型
head -> { prev, data, next } -> { prev, data, next } -> { prev, data, next(tail) } -> null;
- head 指向头部节点; 
- tail 指向尾部节点; 
- prev 指向上一个节点; 
- next 指向下一个节点; 
- data 当前节点数据 
第一个节点的  prev 是 null
最后一个节点的 next 是 null
二、属性和方法
链表的属性:
- head: 头部节点
- node: 节点
- data: 节点数据
- prev:指向下一个节点的
- next:指向下一个节点的
 
- tail: 尾部节点
- length: 链表长度
链表的方法(6个):
- append(data):向链表尾部添加一个新的项
- insert(position, data):向链表的特定位置 插入一个新的项
- get(position):获取对应位置的元素
- indexOf(data):返回元素在链表中的索引。如果链表中没有该元素,则返回-1
- update(position, data):修改某个位置的元素
- removeAt(position):从链表的特定位置删除一项
- remove(data):从列表中删除一项
- getHead():获取头部节点
- forwardString():返回向前遍历的节点字符串形式
- backwordString():返回向后遍历的节点字符串形式
- getTail():获取尾部节点
- isEmpty():如果链表中不包含任何元素,返回true;否则返回false
- size():返回链表中包含的元素个数
- toString():将链表中的内容,转换成字符串形式
具体实现:
| 12
 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
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 
 | function DoublyLinkedList() {
 
 
 function Node(data) {
 this.data = data;
 this.prev = null;
 this.next = null;
 }
 
 
 this.head = null;
 this.tail = null;
 this.length = 0;
 
 
 DoublyLinkedList.prototype.append = function (data) {
 
 const node = new Node(data);
 
 
 if (this.length === 0) {
 this.head = node;
 this.tail = node;
 
 } else {
 
 node.prev = this.tail;
 this.tail.next = node;
 this.tail = node;
 }
 
 this.length++;
 }
 
 
 DoublyLinkedList.prototype.insert = function (position, data) {
 
 
 if (position < 0 || position > this.length) {
 return false
 }
 
 
 const node = new Node(data);
 
 
 if (this.length === 0) {
 this.head = node;
 this.tail = node;
 } else {
 
 
 if (position === 0) {
 this.head.prev = node;
 node.next = this.head;
 this.head = node;
 
 } else if (position === this.length) {
 this.tail.next = node;
 node.prev = this.tail;
 this.tail = node;
 
 } else {
 
 let currentNode = this.head;
 let index = 0;
 
 while (index++ < position) {
 currentNode = currentNode.next;
 }
 
 node.next = currentNode;
 node.prev = currentNode.prev
 
 currentNode.prev.next = node;
 currentNode.prev = node;
 
 }
 }
 
 this.length++;
 return true;
 }
 
 
 DoublyLinkedList.prototype.get = function (position) {
 if (position < 0 || position >= this.length) {
 return null;
 }
 
 let currentNode = this.head;
 let index = 0;
 
 while(index++ < position) {
 currentNode = currentNode.next;
 }
 
 return currentNode.data;
 }
 
 
 DoublyLinkedList.prototype.indexOf = function (data) {
 
 let curentNode = this.head;
 let index = 0;
 
 while(index < this.length) {
 if (curentNode.data === data) {
 return index;
 }
 curentNode = curentNode.next;
 index++
 }
 
 return -1;
 
 }
 
 
 DoublyLinkedList.prototype.update = function (position, data) {
 
 if(position < 0 || position >= this.length) {
 return false;
 }
 
 let curentNode = this.head;
 let index = 0;
 
 while(index++ < position) {
 curentNode = curentNode.next;
 }
 curentNode.data = data;
 return true;
 }
 
 
 DoublyLinkedList.prototype.removeAt = function (position) {
 if(position < 0 || position >= this.length) {
 return null;
 }
 
 let curentNode = this.head;
 
 if (this.length === 1) {
 this.head = null
 this.tail = null;
 } else {
 
 if (position === 0) {
 
 this.head.next.prev = null;
 this.head = this.head.next;
 
 } else if (position === this.length - 1) {
 
 curentNode = this.tail;
 this.tail.prev.next = null;
 this.tail = this.tail.prev;
 
 } else {
 
 
 let index = 0;
 
 while(index++ < position) {
 curentNode = curentNode.next;
 }
 
 curentNode.prev.next = curentNode.next;
 curentNode.next.prev = curentNode.prev;
 }
 }
 
 this.length--;
 
 return curentNode.data;
 }
 
 
 DoublyLinkedList.prototype.remove = function (data) {
 
 const postion = this.indexOf(data);
 
 
 return this.removeAt(postion);
 }
 
 DoublyLinkedList.prototype.getHead = function () {
 return this.head.data;
 }
 
 DoublyLinkedList.prototype.getTail = function () {
 return this.tail.data;
 }
 
 
 DoublyLinkedList.prototype.isEmpty = function () {
 return this.length === 0;
 }
 
 
 DoublyLinkedList.prototype.size = function () {
 return this.length
 }
 
 
 DoublyLinkedList.prototype.toString = function () {
 return this.backwordString();
 }
 
 
 DoublyLinkedList.prototype.forwardString = function () {
 let current = this.tail;
 let listString = '';
 
 while (current) {
 listString += current.data + ' ';
 current = current.prev;
 }
 
 return listString;
 }
 
 
 DoublyLinkedList.prototype.backwordString = function () {
 let current = this.head;
 let listString = '';
 
 while (current) {
 listString += current.data + ' ';
 current = current.next;
 }
 
 return listString;
 }
 }
 
 |