JavaScript 数据结构与算法(七)双向链表
单向链表和双向链表
单向链表
- 只能从头遍历到尾或者从尾遍历到头(一般从头到尾)。
- 链表相连的过程是单向的,实现原理是上一个节点中有指向下一个节点的引用。
- 单向链表有一个比较明显的缺点:可以轻松到达下一个节点,但回到前一个节点很难,在实际开发中,经常会遇到需要回到上一个节点的情况。
双向链表
- 既可以从头遍历到尾,也可以从尾遍历到头。
- 链表相连的过程是双向的。实现原理是一个节点既有向前连接的引用,也有一个向后连接的引用。
- 双向链表可以有效的解决单向链表存在的问题。
- 双向链表缺点:
- 每次在插入或删除某个节点时,都需要处理四个引用,而不是两个,实现起来会困难些。
- 相对于单向链表,所占内存空间更大一些。
- 但是,相对于双向链表的便利性而言,这些缺点微不足道。
双向链表结构
- 双向链表不仅有 head 指针指向第一个节点,而且有 tail 指针指向最后一个节点。
- 每一个节点由三部分组成:item 储存数据、prev 指向前一个节点、next 指向后一个节点。
- 双向链表的第一个节点的 prev 指向 null。
- 双向链表的最后一个节点的 next 指向 null。
双向链表常见的操作
append(element)
向链表尾部追加一个新元素。insert(position, element)
向链表的指定位置插入一个新元素。getElement(position)
获取指定位置的元素。indexOf(element)
返回元素在链表中的索引。如果链表中没有该元素就返回 -1。update(position, element)
修改指定位置上的元素。removeAt(position)
从链表中的删除指定位置的元素。remove(element)
从链表删除指定的元素。isEmpty()
如果链表中不包含任何元素,返回 trun
,如果链表长度大于 0 则返回 false
。size()
返回链表包含的元素个数,与数组的 length
属性类似。toString()
由于链表项使用了 Node 类,就需要重写继承自 JavaScript 对象默认的 toString
方法,让其只输出元素的值。forwardString()
返回正向遍历节点字符串形式。backwordString()
返回反向遍历的节点的字符串形式。
双向链表的封装
创建双向链表类 DoublyLinkedList
- DoublyNode 类继承单向链表的 Node 类,新添加
this.prev
属性,该属性用于指向上一个节点。 - DoublyLinkedList 类继承 LinkedList 类,新添加
this.tail
属性,该属性指向末尾的节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class DoublyNode extends Node { constructor(element) { super(element); this.prev = null; } }
class DoublyLinkedList extends LinkedList { constructor() { super(); this.tail = null; } }
|
append(element)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
append(element) {
const newNode = new DoublyNode(element);
if (this.head === null) { this.head = newNode; this.tail = newNode; } else { this.tail.next = newNode; newNode.prev = this.tail; this.tail = newNode; }
this.length++; }
|
insert(position, element)
1 2 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
|
insert(position, element) { if (position < 0 || position > this.length) return false;
const newNode = new DoublyNode(element);
if (position === 0) {
if (this.head === null) { this.head = newNode; this.tail = newNode; } else { newNode.next = this.head; this.head.perv = newNode; this.head = newNode; }
} else if (position === this.length) {
this.tail.next = newNode; newNode.prev = this.tail; this.tail = newNode; } else {
let targetIndex = 0; let currentNode = this.head; let previousNode = null;
while (targetIndex++ < position) { previousNode = currentNode; currentNode = currentNode.next; }
previousNode.next = newNode; newNode.prev = previousNode;
newNode.next = currentNode; currentNode.prev = newNode; }
this.length++;
return true; }
|
insert(position, element)
1 2 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
|
insert(position, element) { if (position < 0 || position > this.length) return false;
const newNode = new DoublyNode(element);
if (position === 0) {
if (this.head === null) { this.head = newNode; this.tail = newNode; } else { newNode.next = this.head; this.head.perv = newNode; this.head = newNode; }
} else if (position === this.length) {
this.tail.next = newNode; newNode.prev = this.tail; this.tail = newNode; } else {
let targetIndex = 0; let currentNode = this.head; let previousNode = null;
while (targetIndex++ < position) { previousNode = currentNode; currentNode = currentNode.next; }
previousNode.next = newNode; newNode.prev = previousNode;
newNode.next = currentNode; currentNode.prev = newNode; }
this.length++;
return true; }
|
removeAt(position)
1 2 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
|
removeAt(position) { if (position < 0 || position > this.length - 1) return null;
let currentNode = this.head; if (position === 0) {
if (this.length === 1) { this.head = null; this.tail = null; } else { this.head = this.head.next; this.head.prev = null; }
} else if (position === this.length - 1) {
currentNode = this.tail; this.tail.prev.next = null; this.tail = this.tail.prev;
} else {
let targetIndex = 0; let previousNode = null; while (targetIndex++ < position) { previousNode = currentNode; currentNode = currentNode.next; }
previousNode.next = currentNode.next; currentNode.next.perv = previousNode;
}
this.length--; return currentNode.data; }
|
update(position, data)
1 2 3 4 5 6 7 8 9 10
|
update(position, data) { const result = this.removeAt(position);
this.insert(position, data); return result; }
|
forwardToString()
1 2 3 4 5 6 7 8 9 10 11 12 13
| forwardToString() { let currentNode = this.head; let result = '';
while (currentNode) { result += currentNode.data + '--'; currentNode = currentNode.next; }
return result; }
|
backwardString()
1 2 3 4 5 6 7 8 9 10 11 12 13
| backwardString() { let currentNode = this.tail; let result = '';
while (currentNode) { result += currentNode.data + '--'; currentNode = currentNode.prev; }
return result; }
|
其他方法的实现
双向链表的其他方法通过继承单向链表来实现。
完整实现
1 2 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
| class DoublyLinkedList extends LinkedList { constructor() { super(); this.tail = null; }
append(element) { const newNode = new DoublyNode(element);
if (this.head === null) { this.head = newNode; this.tail = newNode; } else { this.tail.next = newNode; newNode.prev = this.tail; this.tail = newNode; }
this.length++; }
insert(position, element) { if (position < 0 || position > this.length) return false;
const newNode = new DoublyNode(element);
if (position === 0) {
if (this.head === null) { this.head = newNode; this.tail = newNode; } else { newNode.next = this.head; this.head.perv = newNode; this.head = newNode; } } else if (position === this.length) {
this.tail.next = newNode; newNode.prev = this.tail; this.tail = newNode; } else {
let targetIndex = 0; let currentNode = this.head; let previousNode = null;
while (targetIndex++ < position) { previousNode = currentNode; currentNode = currentNode.next; }
previousNode.next = newNode; newNode.prev = previousNode;
newNode.next = currentNode; currentNode.prev = newNode; }
this.length++;
return true; }
getData(position) { return super.getData(position); }
indexOf(data) { return super.indexOf(data); }
removeAt(position) { if (position < 0 || position > this.length - 1) return null;
let currentNode = this.head; if (position === 0) {
if (this.length === 1) { this.head = null; this.tail = null; } else { this.head = this.head.next; this.head.prev = null; } } else if (position === this.length - 1) {
currentNode = this.tail; this.tail.prev.next = null; this.tail = this.tail.prev; } else {
let targetIndex = 0; let previousNode = null; while (targetIndex++ < position) { previousNode = currentNode; currentNode = currentNode.next; }
previousNode.next = currentNode.next; currentNode.next.perv = previousNode; }
this.length--; return currentNode.data; }
update(position, data) { const result = this.removeAt(position);
this.insert(position, data); return result; }
remove(data) { return super.remove(data); }
isEmpty() { return super.isEmpty(); }
size() { return super.size(); }
forwardToString() { let currentNode = this.head; let result = "";
while (currentNode) { result += currentNode.data + "--"; currentNode = currentNode.next; }
return result; }
backwardString() { let currentNode = this.tail; let result = "";
while (currentNode) { result += currentNode.data + "--"; currentNode = currentNode.prev; }
return result; } }
|
代码测试
1 2 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
| const doublyLinkedList = new DoublyLinkedList();
doublyLinkedList.append("ZZ"); doublyLinkedList.append("XX"); doublyLinkedList.append("CC"); console.log(doublyLinkedList);
doublyLinkedList.insert(0, "00"); doublyLinkedList.insert(2, "22"); console.log(doublyLinkedList);
console.log(doublyLinkedList.getData(1));
console.log(doublyLinkedList.indexOf("XX")); console.log(doublyLinkedList);
doublyLinkedList.removeAt(0); doublyLinkedList.removeAt(1); console.log(doublyLinkedList);
doublyLinkedList.update(0, "111111"); console.log(doublyLinkedList);
console.log(doublyLinkedList.remove("111111")); console.log(doublyLinkedList.remove("22222")); console.log(doublyLinkedList);
console.log(doublyLinkedList.forwardToString());
console.log(doublyLinkedList.backwardString());
|
JavaScript 数据结构与算法(七)双向链表
post/5485c8ad25cf/
Published
2020-07-25 16:22