不同于单链表,双向链表每个节点除了蕴含数据以外,还包含了别离指向其前节点和后节点的指针
其实双链表与比单链表相比,就是节点上新增了一个指向上一个节点的指针而已
上面我来手写一下双链表,实现一些办法:
<img src="https://pic4.zhimg.com/80/v2-1aa41e01531c94ab3e843da7e831049b_720w.png" alt="img" style="zoom:80%;" />
咱们先实现一个节点构造函数:
class Node { constructor(element) { this.element = element; // 节点数据值 this.next = null; // 前指针 this.prev = null; // 后指针 }}
双向链表整体构造
class DoubleLinkedList { constructor() { this.count = 0; // 记录节点数 this.head = null; // 双向链表头部 this.tail = null; // 双向链表尾部 } // 链表尾部增加节点 add(element) {} // 链表指定地位增加一个节点 addAt(index, element) {} // 移除链表指定地位的节点 removeAt(index) {} // 移除链表指定节点 remove(element) {} // 翻转链表 reverse() {} // 替换两个节点的地位 swap() {} // 遍历链表 traverse() {} // 查找某个节点的索引 find() {} // 查问链表是否为空 isEmpty() {} // 查问链表的长度 length() {}}
接下来就是实现这一个个的办法了
依据地位(索引)查找节点,接下来的办法会始终用到这个办法,索性将其封装成一个办法,以便调用
getElement(index) { if (index >= 0 && index < this.count) { let node = this.head; // 循环遍历到该节点 for (let i = 0; i < index; i++) { node = node.next; } return node; } return undefined;}
1、增加节点
<img src="https://pic4.zhimg.com/80/v2-d71171007ed06c66e076048e51184694_720w.png" alt="img" style="zoom:80%;" />
1.1在尾部增加节点
// 传入一个节点add(element) { // 先创立一个节点 const node = new Node(element); // 如果链表为空,那就是头部节点和尾部节点都是node了 if (this.head === null && this.tail === null) { this.head = node; this.tail = node } // 如果链表已有头部,则在其尾部增加节点即可 else { node.prev = this.tail; this.tail.next = node; this.tail = node; } // 节点总数+1 this.count++;}
1.2 链表指定地位增加一个节点
addAt(index, element) { if (index >= 0 && index <= this.count) { const node = new Node(element); // 当在头部插入时 if (index === 0) { this.head.prev = node; node.next = this.head; this.head = node; } // 在尾部插入节点 else if (index === this.count) { this.add(element) this.count--; } // 在两头插入节点 else { let current = this.getElement(index); // 先设置新增节点与前一个节点的关系 node.prev = current.prev; current.prev.next = node; // 再设置新增与后一个节点的关系 node.next = current; current.prev = node } this.count++; return true } return false;}
2、移除节点
<img src="https://pic1.zhimg.com/80/v2-90d433c7a304d9614996bc26eafea438_720w.png" alt="img" style="zoom:80%;" />
2.1、移除链表指定地位的节点
removeAt(index) { if (index >= 0 && index < this.count) { // 移除头节点 if (index === 0) { this.head = this.head.next; this.head.prev = null; } // 移除尾部节点 else if (index === this.count - 1) { this.tail = this.tail.prev; this.tail.next = null; } // 移除两头节点 else { let current = this.getElement(index); current.next.prev = current.prev current.prev.next = current.next } this.count--; return true } return undefined;}
2.2、移除链表指定节点
移除指定节点,首先就是要找到该节点
remove(element) { let current = this.head; while (current) { if (current === element) { // 链表只有一个节点 if (current === this.head && current === this.prev) { this.head = null; this.tail = null; } // 移除头节点 else if (current === this.head) { this.head = this.head.next; this.head.prev = null; } // 移除尾部节点 else if (current === this.tail) { this.tail = this.tail.prev; this.tail.next = null; } // 移除两头节点 else { current.next.prev = current.prev; current.prev.next = current.next; } this.count--; } current = current.next; }}
5、翻转链表
reverse() { let current = this.head; let prev = null; // 先将两头的 while (current) { let next = current.next; // 前后倒置 current.next = prev; current.prev = next; prev = current; current = next } this.tail = this.head; this.head = prev}
6、替换两个节点的地位
swap(index1, index2) { if (index1 >= 0 && index1 < this.count && index2 >= 0 && index2 < this.count) { let node1 = this.getElement(index1) let node2 = this.getElement(index2) // 让index1 始终小于 index2,不便前面的查找替换 if (index1 > index2) { return this.swap(index2, index1) } // 替换两个节点的数值 [node1.element, node2.element] = [node2.element, node1.element] return true } return undefined;}
7、遍历链表
display() { if (!this.isEmpty()) { let current = this.head; let result = ''; while (current) { result += current.element current = current.next if (current) { result += '->'; } } return result; } return undefined;}
8、查找某个节点的索引
find(element) { let current = this.head; let index = 0 while (current) { if (current === element) { return index; } current = current.next; index++; } return undefined}
9、查问链表是否为空
isEmpty() { return this.count === 0}
10、查问链表的长度
length() { return this.count;}
整合一下:
// 实现一个节点构造函数class Node { constructor(element) { this.element = element; this.next = null; this.prev = null; }}// 双向链表class DoubleLinkedList { constructor() { this.count = 0; // 记录节点数 this.head = null; // 双向链表头部 this.tail = null; // 双向链表尾部 } // 遍历一个办法,循环到指标地位 getElement(index) { if (index >= 0 && index < this.count) { let node = this.head; for (let i = 0; i < index; i++) { node = node.next; } return node; } return undefined; } // 链表尾部增加节点 add(element) { // 先创立一个节点 const node = new Node(element); // 如果链表为空 if (this.head === null && this.tail === null) { this.head = node; this.tail = node } // 如果链表已有头部,则在其尾部增加节点即可 else { node.prev = this.tail; this.tail.next = node; this.tail = node; } this.count++; } // 链表指定地位增加一个节点 addAt(index, element) { if (index >= 0 && index <= this.count) { const node = new Node(element); // 当在头部插入时 if (index === 0) { this.head.prev = node; node.next = this.head; this.head = node; } else if (index === this.count) { this.add(element) this.count--; } // 非头部插入时 else { let current = this.getElement(index); // 先设置新增节点与前一个节点的关系 node.prev = current.prev; current.prev.next = node; // 再设置新增与后一个节点的关系 node.next = current; current.prev = node } this.count++; return true } return false; } // 移除链表指定地位的节点 removeAt(index) { if (index >= 0 && index < this.count) { // 移除头节点 if (index === 0) { this.head = this.head.next; this.head.prev = null; } // 移除尾部节点 else if (index === this.count - 1) { this.tail = this.tail.prev; this.tail.next = null; } // 移除两头节点 else { let current = this.getElement(index); current.next.prev = current.prev current.prev.next = current.next } this.count--; return true } return undefined; } // 移除链表指定节点 remove(element) { let current = this.head; while (current) { if (current === element) { // 链表只有一个节点 if (current === this.head && current === this.prev) { this.head = null; this.tail = null; } // 移除头节点 else if (current === this.head) { this.head = this.head.next; this.head.prev = null; } // 移除尾部节点 else if (current === this.tail) { this.tail = this.tail.prev; this.tail.next = null; } // 移除两头节点 else { current.next.prev = current.prev; current.prev.next = current.next; } this.count--; } current = current.next; } } // 翻转链表 reverse() { let current = this.head; let prev = null; while (current) { let next = current.next; // 前后倒置 current.next = prev; current.prev = next; prev = current; current = next } this.tail = this.head; this.head = prev } // 替换两个节点的地位 swap(index1, index2) { if (index1 >= 0 && index1 < this.count && index2 >= 0 && index2 < this.count) { let node1 = this.getElement(index1) let node2 = this.getElement(index2) // 让index1 始终小于 index2,不便前面的查找替换 if (index1 > index2) { return this.swap(index2, index1) } // 替换两个节点的数值 [node1.element, node2.element] = [node2.element, node1.element] return true } return undefined; } // 遍历链表 display() { if (!this.isEmpty()) { let current = this.head; let result = ''; while (current) { result += current.element current = current.next if (current) { result += '->'; } } return result; } return undefined; } // 查找某个节点的索引 find(element) { let current = this.head; let index = 0 while (current) { if (current === element) { return index; } current = current.next; index++; } return undefined } // 查问链表是否为空 isEmpty() { return this.count === 0 } // 查问链表的长度 length() { return this.count; }}
咱们来调用一下:
// 先实例化一个双向链表对象let DLL = new DoubleLinkedList();// 尾部增加DLL.add(1);DLL.add(2);// 任意地位增加DLL.addAt(0, 3);DLL.addAt(3, 6);// 遍历console.log(DLL.display()); // 3->1->2->6// 依据地位删除DLL.removeAt(3);// 替换两个节点DLL.swap(0, 2)console.log(DLL.display()); // 2->1->3// 翻转DLL.reverse();// 链表是否为空console.log(DLL.isEmpty()); // false// 链表长度console.log(DLL.length()); // 3
总结:
- 所以你会发现实现一个链表无非也就是增(增加节点)删(删除节点)改(替换)查(遍历)
- 增加和删除节点无非就是批改节点的指针指向而已