队列
队列是遵循先进先出(FIFO,也称为先来先服务)原则的一组有序的项。队列在尾部添加新元素,并从顶部移除元素。最新添加的元素必须排在队列的末尾
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
| class Queue { constructor() { this.count = 0; this.lowestCount = 0; this.items = {}; } enqueue(element) { this.items[this.count] = element; this.count++; } dequeue() { if (this.isEmpty()) { return undefined; } const result = this.items[this.lowestCount]; delete this.items[this.lowestCount]; this.lowestCount++; return result; } peek() { if (this.isEmpty()) { return undefined; } return this.items[this.lowestCount]; } isEmpty() { return this.count - this.lowestCount === 0; } size() { return this.count - this.lowestCount; } clear() { this.items = {}; this.count = 0; this.lowestCount = 0; } toString() { if (this.isEmpty()) { return ''; } let objString = `${this.items[this.lowestCount]}`; for (let i = this.lowestCount + 1; i < this.count; i++) { objString = `${objString},${this.items[i]}`; } return objString; } }
|
双端队列
双端队列(deque,或称 double-ended queue)是一种允许我们同时从前端和后端添加和移除元素的特殊队列
由于双端队列同时遵守了先进先出和后进先出原则,可以说它是把队列和栈相结合的一种数据结构
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
| class Deque { constructor() { this.count = 0; this.lowestCount = 0; this.items = {}; } addFront(element) { if (this.isEmpty()) { this.addBack(element); } else if (this.lowestCount > 0) { this.lowestCount--; this.items[this.lowestCount] = element; } else { for (let i = this.count; i > 0; i--) { this.items[i] = this.items[i - 1]; } this.count++; this.lowestCount = 0; this.items[0] = element; } } addBack(element) { this.items[this.count] = element; this.count++; } removeFront() { if (this.isEmpty()) { return undefined; } const result = this.items[this.lowestCount]; delete this.items[this.lowestCount]; this.lowestCount++; return result; } removeBack() { if (this.isEmpty()) { return undefined; } this.count--; const result = this.items[this.count]; delete this.items[this.count]; return result; } peekFront() { if (this.isEmpty()) { return undefined; } return this.items[this.lowestCount]; } peekBack() { if (this.isEmpty()) { return undefined; } return this.items[this.count - 1]; } isEmpty() { return this.count - this.lowestCount === 0; } clear() { this.count = 0; this.lowestCount = 0; this.items = {}; } }
|
循环队列 - 击鼓传花
场景:在这个游戏中,孩子们围成一个圆圈,把花尽快地传递给旁边的人。某一时刻传花停止,这个时候花在谁手里,谁就退出圆圈、结束游戏。重复这个过程,直到只剩一个孩子(胜者)
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
| function hotPotato(elementsList, num) { const queue = new Queue(); const elimitatedList = []; for (let i = 0; i < elementsList.length; i++) { queue.enqueue(elementsList[i]); } while (queue.size() > 1) { for (let i = 0; i < num; i++) { queue.enqueue(queue.dequeue()); } elimitatedList.push(queue.dequeue()); } return { eliminated: elimitatedList, winner: queue.dequeue() }; }
const names = ['John', 'Jack', 'Camila', 'Ingrid', 'Carl']; const result = hotPotato(names, 7); result.eliminated.forEach(name => { console.log(`${name}在击鼓传花游戏中被淘汰。`); }); console.log(`胜利者: ${result.winner}`);
|
回文检查器
回文:回文是正反都能读通的单词、词组、数或一系列字符的序列,例如 madam或 racecar。
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
| function palindromeChecker(aString) { if (aString === undefined || aString === null || (aString !== null && aString.length === 0)) { return false; } const deque = new Deque(); const lowerString = aString.toLocaleLowerCase().split(' ').join(''); let isEqual = true; let firstChar, lastChar; for (let i = 0; i < lowerString.length; i++) { deque.addBack(lowerString.charAt(i)); }
while (deque.size() > 1 && isEqual) { firstChar = deque.removeFront(); lastChar = deque.removeBack();
if (firstChar !== lastChar) { isEqual = false; } } return isEqual; }
|
javascript事件循环
链表
链表数据结构

链表的好处:添加或移除元素的时候不需要移动其他元素
要想访问链表中间的一个元素,需要从起点(表头)开始迭代链表直到找到所需的元素
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
| export function defaultEquals(a, b) { return a === b; }
export class Node { constructor(element) { this.element = element; this.next = undefined; } }
import { defaultEquals } from '../util'; import { Node } from './models/linked-list-models'; export default class LinkedList { constructor(equalsFn = defaultEquals) { this.count = 0; this.head = undefined; this.equalsFn = equalsFn; } push(element) { const node = new Node(element); let current;
if (this.head == null) { this.head = node; } else { current = this.head; while (current.next != null) { current = current.next; } current.next = node; } this.count++; } removeAt(index) { if (index >= 0 && index < this.count) { let current = this.head; if (index === 0) { this.head = current.next; } else { const previous = this.getElementAt(index - 1); current = previous.next; previous.next = current.next; } this.count--; return current.element; } return undefined; } getElementAt(index) { if (index >= 0 && index <= this.count) { let node = this.head; for (let i = 0; i < index && node != null; i++) { node = node.next; } return node; } return undefined; } insert(element, index) { if (index >= 0 && index <= this.count) { const node = new Node(element);
if (index === 0) { const current = this.head; node.next = current; this.head = node; } else { const previous = this.getElementAt(index - 1); const current = previous.next; node.next = current; previous.next = node; } this.count++; return true; } return false; } indexOf(element) { let current = this.head; for (let i = 0; i < this.count && current != null; i++) { if (this.equalsFn(element, current.element)) { return i; } current = current.next; } return -1; } remove(element) { const index = this.indexOf(element); return this.removeAt(index); } size() { return this.count; } isEmpty() { return this.count === 0; } getHead() { return this.head; } toString() { if (this.head == null) { return ''; } let objString = `${this.head.element}`; let current = this.head.next;
for (let i = 1; i < this.size() && current != null; i++) { objString = `${objString},${current.element}`; current = current.next; } return objString; } }
|
javascript垃圾回收器
双向链表
在双向链表中,链接是双向的:一个链向下一个元素,另一个链向前一个元素

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
| class DoublyNode extends Node { constructor(element, next, prev) { super(element, next); this.prev = prev; } }
class DoublyLinkedList extends LinkedList { constructor(equalsFn = defaultEquals) { super(equalsFn); this.tail = undefined; } insert(element, index) { if (index >= 0 && index <= this.count) { const node = new DoublyNode(element); let current = this.head;
if (index === 0) { if (this.head == null) { this.head = node; this.tail = node; } else { node.next = this.head; current.prev = node; this.head = node; } } else if (index === this.count) { current = this.tail; current.next = node; node.prev = current; this.tail = node; } else { const previous = this.getElementAt(index - 1); current = previous.next; node.next = current; previous.next = node; current.prev = node; node.prev = previous; } this.count++; return true; } return false; } removeAt(index) { if (index >= 0 && index < this.count) { let current = this.head;
if (index === 0) { this.head = current.next; if (this.count === 1) { this.tail = undefined; } else { this.head.prev = undefined; } } else if (index === this.count - 1) { current = this.tail; this.tail = current.prev; this.tail.next = undefined; } else { current = this.getElementAt(index); const previous = current.prev; previous.next = current.next; current.next.prev = previous; } this.count--; return current.element; } return undefined; } }
|
循环链表
循环链表可以像链表一样只有单向引用,也可以像双向链表一样有双向引用。循环链表和链表之间唯一的区别在于,最后一个元素指向下一个元素的指针(tail.next)不是引用undefined,而是指向第一个元素(head)
双向循环链表有指向 head 元素的 tail.next 和指向 tail 元素的 head.prev


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
| class CircularLinkedList extends LinkedList { constructor(equalsFn = defaultEquals) { super(equalsFn); } insert(element, index) { if (index >= 0 && index <= this.count) { const node = new Node(element); let current = this.head;
if (index === 0) { if (this.head == null) { this.head = node; node.next = this.head; } else { node.next = current; current = this.getElementAt(this.size()); this.head = node; current.next = this.head; } } else { const previous = this.getElementAt(index - 1); node.next = previous.next; previous.next = node; } this.count++; return true; } return false; } removeAt(index) { if (index >= 0 && index < this.count) { let current = this.head;
if (index === 0) { if (this.size() === 1) { this.head = undefined; } else { const removed = this.head; current = this.getElementAt(this.size()); this.head = this.head.next; current.next = this.head; current = removed; } } else { const previous = this.getElementAt(index - 1); current = previous.next; previous.next = current.next; } this.count--; return current.element; } return undefined; } }
|
有序链表
有序链表是指保持元素有序的链表结构。除了使用排序算法之外,我们还可以将元素插入到正确的位置来保证链表的有序性。
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
| const Compare = { LESS_THAN: -1, BIGGER_THAN: 1 }; function defaultCompare(a, b) { if (a === b) { return 0; } return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; } class SortedLinkedList extends LinkedList { constructor(equalsFn = defaultEquals, compareFn = defaultCompare) { super(equalsFn); this.compareFn = compareFn; } insert(element, index = 0) { if (this.isEmpty()) { return super.insert(element, 0); } const pos = this.getIndexNextSortedElement(element); return super.insert(element, pos); } getIndexNextSortedElement(element) { let current = this.head; let i = 0; for (; i < this.size() && current; i++) { const comp = this.compareFn(element, current.element); if (comp === Compare.LESS_THAN) { return i; } current = current.next; } return i; } }
|
创建 StackLinkedList 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class StackLinkedList { constructor() { this.items = new DoublyLinkedList(); } push(element) { this.items.push(element); } pop() { if (this.isEmpty()) { return undefined; } return this.items.removeAt(this.size() - 1); } }
|