链表
多图预警:




/*jshint esversion: 6 */
/**
* js中数组缺点: js中数组被实现为一个对象, 执行效率低下。
* 链表:
* 1. 执行效率高, 除了不能队数据进行随机访问, 适合于任意情况下的一维数组。
* 2. 是由一组节点组成的集合,每个节点都有一个对象引用指向它的后继,指向另外一个节点的引用叫做链。
*
*/
// 遍历链表: 跟随链, 从链表的首部遍历到尾部,不包括头节点(头节点一般用作链表的接入点)。
/**
* @desc: 节点类
*
* @ADT模型
* element属性: 节点元素
* next属性: 指向后继节点的引用
*/
class Node {
constructor(element) {
this.element = element;
this.next = null;
}
}
/**
* @desc: 链表类
* @ADT模型
*
* find(element): 根据element查找其在链表中的节点
* insert(): 在已知节点后面向链表插入一个元素
* remove(): 删除一个节点
* display(): 显示链表中的元素
* advance(n): 使当前节点向前移动 n 个节点
* show(): 显示当前节点上的数据
*/
class LinkedList {
constructor() {
// 头节点
this.head = new Node('head');
// 当前节点
this.current_node = this.head;
}
// 搜索给定节点的前一个节点
_findPrevious(element) {
let current_node = this.head;
while ((current_node.next !== null) && (current_node.next.element !== element)) {
current_node = current_node.next;
}
return current_node;
}
find(element) {
let current_node = this.head;
while (current_node.element !== element) {
current_node = current_node.next;
}
return current_node;
}
insert(newElement, element) {
const newElementNode = new Node(newElement);
const current_node = this.find(element);
// 交互next引用
newElementNode.next = current_node.next;
current_node.next = newElementNode;
}
remove(element) {
const prevNode = this._findPrevious(element);
if (prevNode.next !== null) {
prevNode.next = prevNode.next.next;
}
}
display() {
let current_node = this.head;
while (current_node.next !== null) {
console.log(current_node.next.element);
current_node = current_node.next;
}
}
advance(n) {
if (n <= 0) {
return;
}
let index = 0;
while (index < n && this.current_node.next !== null) {
this.current_node = this.current_node.next;
index++;
}
}
show () {
return this.current_node.element;
}
}
// test code
const linkedList = new LinkedList();
linkedList.insert("Conway", "head");
linkedList.insert("Russellville", "Conway");
linkedList.insert("Alma", "Russellville");
linkedList.display();
linkedList.remove("Russellville");
console.log('-----remove---------');
linkedList.display();
console.log('-----default---------');
console.log(linkedList.show());
console.log('-----advance 2---------');
linkedList.advance(2);
console.log(linkedList.show());
module.exports = LinkedList;双向链表

/*jshint esversion: 6 */
/**
* @desc: 双向链表
* 正向/反向遍历更方便
*/
/**
* @desc: 节点类
*
* @ADT模型
* element属性: 节点元素
* next属性: 指向后继节点的引用
* previous属性: 指向前驱节点引用
*/
class Node {
constructor(element) {
this.element = element;
this.next = null;
this.previous = null;
}
}
/**
* @desc: 双向链表类
* @ADT模型
*
* find(element): 根据element查找其在链表中的节点
* insert(): 在已知节点后面向链表插入一个元素
* remove(): 删除一个节点
* display(): 显示链表中的元素
* dispReverse(): 反序显示链表中元素
* back(n): 在双向链表中向后移动 n 个节点
* show(): 只显示当前节点
* advance(n): 使当前节点向前移动 n 个节点
*/
class DLinkedList {
constructor() {
// 头节点
this.head = new Node('head');
// 当前节点
this.current_node = this.head;
}
// 搜索最后一个节点
_findLastNode() {
let current_node = this.head;
while (current_node.next !== null) {
current_node = current_node.next;
}
return current_node;
}
find(element) {
let current_node = this.head;
while (current_node.element !== element) {
current_node = current_node.next;
}
return current_node;
}
insert(newElement, element) {
const newElementNode = new Node(newElement);
const current_node = this.find(element);
// 交互next,previous引用
newElementNode.next = current_node.next;
newElementNode.previous = current_node;
current_node.next = newElementNode;
}
remove(element) {
// 删除比单链的删除效率高, 不需要搜索前一个节点了。
const current_node = this.find(element);
if (current_node.next !== null) {
current_node.previous.next = current_node.next;
current_node.next.previous = current_node.previous;
current_node.next = null;
current_node.previous = null;
}
}
display() {
let current_node = this.head;
while (current_node.next !== null) {
console.log(current_node.next.element);
current_node = current_node.next;
}
}
dispReverse() {
let lastNode = this._findLastNode();
while (lastNode.previous !== null) {
console.log(lastNode.element)
lastNode = lastNode.previous;
}
}
advance(n) {
if (n <= 0) {
return;
}
let index = 0;
while (index < n && this.current_node.next !== null) {
this.current_node = this.current_node.next;
index++;
}
}
back (n) {
if (n <= 0) {
return;
}
let index = 0;
while (index < n && this.current_node.previous !== null) {
this.current_node = this.current_node.previous;
index++;
}
}
show () {
return this.current_node.element;
}
}
// test code
const cities = new DLinkedList();
cities.insert("Conway", "head");
cities.insert("Russellville", "Conway");
cities.insert("Carlisle", "Russellville");
cities.insert("Alma", "Carlisle");
cities.display();
console.log('------');
cities.remove("Carlisle");
cities.display();
console.log('------');
cities.dispReverse();
console.log('-----default---------');
console.log(cities.show());
console.log('-----advance 3---------');
cities.advance(3);
console.log(cities.show());
console.log('-----back 2---------');
cities.back(2);
console.log(cities.show());
module.exports = DLinkedList;循环链表

/*jshint esversion: 6 */
/**
*
* @desc: 循环链表: 创建节点时候, 使next都指向头节点, 形成一个闭环。
* 循环链表也可以很方便从后往前遍历, 但是创建循环链表的开销小于双向链表。
*/
/**
* @desc: 节点类
*
* @ADT模型
* element属性: 节点元素
* next属性: 指向后继节点的引用
*/
class Node {
constructor(element) {
this.element = element;
this.next = null;
}
}
/**
* @desc: 循环链表类
* @ADT模型
*
* find(element): 根据element查找其在链表中的节点
* insert(): 在已知节点后面向链表插入一个元素
* remove(): 删除一个节点
* display(): 显示链表中的元素
*/
class LoopLinkedList {
constructor() {
// 头节点
this.head = new Node('head');
// 每个节点的next都指向头节点
this.head.next = this.head;
}
// 搜索给定节点的前一个节点
_findPrevious(element) {
let current_node = this.head;
while ((current_node.next !== null) && (current_node.next.element !== element)) {
current_node = current_node.next;
}
return current_node;
}
find(element) {
let current_node = this.head;
while (current_node.element !== element) {
current_node = current_node.next;
}
return current_node;
}
insert(newElement, element) {
const newElementNode = new Node(newElement);
const current_node = this.find(element);
// 交互next引用
newElementNode.next = current_node.next;
current_node.next = newElementNode;
}
remove(element) {
const prevNode = this._findPrevious(element);
if (prevNode.next !== null) {
prevNode.next = prevNode.next.next;
}
}
display() {
let current_node = this.head;
while ((current_node.next !== null) && (current_node.next.element !== 'head')) {
console.log(current_node.next.element);
current_node = current_node.next;
}
}
}
// test code
const loopLinkedList = new LoopLinkedList();
loopLinkedList.insert("Conway", "head");
loopLinkedList.insert("Russellville", "Conway");
loopLinkedList.insert("Alma", "Russellville");
loopLinkedList.display();
loopLinkedList.remove("Russellville");
console.log('-----remove---------');
loopLinkedList.display();
module.exports = LoopLinkedList;实践
/*jshint esversion: 6 */
// 循环链接解决约瑟夫环问题
/**
* 写一段程序将 n 个人围成一圈,并且第 m 个人会被杀掉,计算 一圈人中哪两个人最后会存活。
* 使用循环链表解决该问题。
*/
class Node {
constructor(element) {
this.element = element;
this.next = null;
}
}
class LoopLinkedList {
constructor() {
// 头节点
this.head = new Node('head');
// 每个节点的next都指向头节点
this.head.next = this.head;
// 当前节点
this.currentNode = this.head;
}
// 搜索给定节点的前一个节点
_findPrevious(element) {
let current_node = this.head;
while ((current_node.next !== null) && (current_node.next.element !== element)) {
current_node = current_node.next;
}
return current_node;
}
find(element) {
let current_node = this.head;
while (current_node.element !== element) {
current_node = current_node.next;
}
return current_node;
}
insert(newElement, element) {
const newElementNode = new Node(newElement);
const current_node = this.find(element);
// 交互next引用
newElementNode.next = current_node.next;
current_node.next = newElementNode;
}
remove(element) {
const prevNode = this._findPrevious(element);
if (prevNode.next !== null) {
prevNode.next = prevNode.next.next;
}
}
display() {
let current_node = this.head;
while ((current_node.next !== null) && (current_node.next.element !== 'head')) {
console.log(current_node.next.element);
current_node = current_node.next;
}
}
// 往前移动n
advance(n) {
if (n <= 0) {
return;
}
while (n > 0) {
if (this.currentNode.next.element == 'head') {
this.currentNode = this.currentNode.next.next;
} else {
this.currentNode = this.currentNode.next;
}
n--;
}
}
// 计算链表个数
count () {
let num = 0;
let current_node = this.head;
while ((current_node.next !== null) && (current_node.next.element !== 'head')) {
current_node = current_node.next;
num++;
}
return num;
}
}
// 假设10个人 3个一循环
const list = new LoopLinkedList();
for (let i = 1; i <= 10; i++) {
if (i === 1) {
list.insert(i, 'head');
} else {
list.insert(i, i - 1);
}
}
console.log('count', list.count())
while (list.count() >= 3) {
list.advance(3);
list.remove(list.currentNode.element);
}
list.display(); // 4, 10