本文對應的JS數據結構及使用方法node
class Stack<T> { private count: number; private items: any; constructor() { this.count = 0; this.items = {}; } push(element: T) { this.items[this.count] = element; this.count++; } pop() { if (this.isEmpty()) { return undefined; } this.count--; const result = this.items[this.count]; delete this.items[this.count]; return result; } peek() { if (this.isEmpty()) { return undefined; } return this.items[this.count - 1]; } isEmpty() { return this.count === 0; } size() { return this.count; } clear() { /* while (!this.isEmpty()) { this.pop(); } */ this.items = {}; this.count = 0; } toString() { if (this.isEmpty()) { return ''; } let objString = `${this.items[0]}`; for (let i = 1; i < this.count; i++) { objString = `${objString},${this.items[i]}`; } return objString; } }
class Queue<T> { private count: number; private lowestCount: number; private items: any; constructor() { this.count = 0; this.lowestCount = 0; this.items = {}; } enqueue(element: T) { 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.size() === 0; } clear() { this.items = {}; this.count = 0; this.lowestCount = 0; } size() { return this.count - this.lowestCount; } 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; } }
class Deque<T> { private count: number; private lowestCount: number; private items: any; constructor() { this.count = 0; this.lowestCount = 0; this.items = {}; } addFront(element: T) { 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.items[0] = element; } } addBack(element: T) { 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.size() === 0; } clear() { this.items = {}; this.count = 0; this.lowestCount = 0; } size() { return this.count - this.lowestCount; } 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; } }
class Node<T> { constructor(public element: T, public next?: Node<T>) {} } function defaultEquals<T>(a: T, b: T): boolean { return a === b; } type IEqualsFunction<T> = (a: T, b: T) => boolean;
class LinkedList<T> { protected count = 0; protected head: Node<T> | undefined; constructor(protected equalsFn: IEqualsFunction<T> = defaultEquals) {} push(element: T) { const node = new Node(element); let current; if (this.head == null) { // catches null && undefined this.head = node; } else { current = this.head; while (current.next != null) { current = current.next; } current.next = node; } this.count++; } getElementAt(index: number) { 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: T, index: number) { 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); node.next = previous.next; previous.next = node; } this.count++; return true; } return false; } removeAt(index: number) { 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; } remove(element: T) { const index = this.indexOf(element); return this.removeAt(index); } indexOf(element: T) { let current = this.head; for (let i = 0; i < this.size() && current != null; i++) { if (this.equalsFn(element, current.element)) { return i; } current = current.next; } return -1; } isEmpty() { return this.size() === 0; } size() { return this.count; } getHead() { return this.head; } clear() { this.head = undefined; this.count = 0; } 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; } }
class DoublyNode<T> extends Node<T> { constructor( public element: T, public next?: DoublyNode<T>, public prev?: DoublyNode<T> ) { super(element, next); } } function defaultEquals<T>(a: T, b: T): boolean { return a === b; } type IEqualsFunction<T> = (a: T, b: T) => boolean;
class DoublyLinkedList<T> extends LinkedList<T> { protected head: DoublyNode<T> | undefined; protected tail: DoublyNode<T> | undefined; constructor(protected equalsFn: IEqualsFunction<T> = defaultEquals) { super(equalsFn); } push(element: T) { const node = new DoublyNode(element); if (this.head == null) { this.head = node; this.tail = node; // NEW } else { // attach to the tail node // NEW this.tail.next = node; node.prev = this.tail; this.tail = node; } this.count++; } insert(element: T, index: number) { if (index >= 0 && index <= this.count) { const node = new DoublyNode(element); let current = this.head; if (index === 0) { if (this.head == null) { // NEW this.head = node; this.tail = node; } else { node.next = this.head; this.head.prev = node; // NEW this.head = node; } } else if (index === this.count) { // last item // NEW current = this.tail; // {2} 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; // NEW node.prev = previous; // NEW } this.count++; return true; } return false; } removeAt(index: number) { if (index >= 0 && index < this.count) { let current = this.head; if (index === 0) { this.head = this.head.next; // {1} // if there is only one item, then we update tail as well //NEW if (this.count === 1) { // {2} this.tail = undefined; } else { this.head.prev = undefined; // {3} } } else if (index === this.count - 1) { // last item //NEW current = this.tail; // {4} this.tail = current.prev; this.tail.next = undefined; } else { current = this.getElementAt(index); const previous = current.prev; // link previous with current's next - skip it to remove previous.next = current.next; // {6} current.next.prev = previous; // NEW } this.count--; return current.element; } return undefined; } indexOf(element: T) { let current = this.head; let index = 0; while (current != null) { if (this.equalsFn(element, current.element)) { return index; } index++; current = current.next; } return -1; } getHead() { return this.head; } getTail() { return this.tail; } clear() { super.clear(); this.tail = undefined; } toString() { if (this.head == null) { return ''; } let objString = `${this.head.element}`; let current = this.head.next; while (current != null) { objString = `${objString},${current.element}`; current = current.next; } return objString; } inverseToString() { if (this.tail == null) { return ''; } let objString = `${this.tail.element}`; let previous = this.tail.prev; while (previous != null) { objString = `${objString},${previous.element}`; previous = previous.prev; } return objString; } }
class Node<T> { constructor(public element: T, public next?: Node<T>) {} } function defaultEquals<T>(a: T, b: T): boolean { return a === b; } type IEqualsFunction<T> = (a: T, b: T) => boolean;
class CircularLinkedList<T> extends LinkedList<T> { constructor(protected equalsFn: IEqualsFunction<T> = defaultEquals) { super(equalsFn); } push(element: T) { const node = new Node(element); let current; if (this.head == null) { this.head = node; } else { current = this.getElementAt(this.size() - 1); current.next = node; } // set node.next to head - to have circular list node.next = this.head; this.count++; } insert(element: T, index: number) { if (index >= 0 && index <= this.count) { const node = new Node(element); let current = this.head; if (index === 0) { if (this.head == null) { // if no node in list this.head = node; node.next = this.head; } else { node.next = current; current = this.getElementAt(this.size()); // update last element 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: number) { 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() - 1); this.head = this.head.next; current.next = this.head; current = removed; } } else { // no need to update last element for circular list const previous = this.getElementAt(index - 1); current = previous.next; previous.next = current.next; } this.count--; return current.element; } return undefined; } }
enum Compare { LESS_THAN = -1, BIGGER_THAN = 1, EQUALS = 0 } function defaultCompare<T>(a: T, b: T): number { if (a === b) { return Compare.EQUALS; } return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; } function defaultEquals<T>(a: T, b: T): boolean { return a === b; } type IEqualsFunction<T> = (a: T, b: T) => boolean; type ICompareFunction<T> = (a: T, b: T) => number;
class SortedLinkedList<T> extends LinkedList<T> { constructor( protected equalsFn: IEqualsFunction<T> = defaultEquals, protected compareFn: ICompareFunction<T> = defaultCompare ) { super(equalsFn); } push(element: T) { if (this.isEmpty()) { super.push(element); } else { const index = this.getIndexNextSortedElement(element); super.insert(element, index); } } insert(element: T, index: number = 0) { if (this.isEmpty()) { return super.insert(element, 0); } index = this.getIndexNextSortedElement(element); return super.insert(element, index); } private getIndexNextSortedElement(element: T) { 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; } }
class Set<T> { private items: any; constructor() { this.items = {}; } add(element: T) { if (!this.has(element)) { this.items[element] = element; return true; } return false; } delete(element: T) { if (this.has(element)) { delete this.items[element]; return true; } return false; } has(element: T) { // return this.items.hasOwnProperty(element); return Object.prototype.hasOwnProperty.call(this.items, element); } values(): T[] { return Object.values(this.items); } union(otherSet: Set<T>) { const unionSet = new Set<T>(); this.values().forEach(value => unionSet.add(value)); otherSet.values().forEach(value => unionSet.add(value)); return unionSet; } intersection(otherSet: Set<T>) { const intersectionSet = new Set<T>(); const values = this.values(); const otherValues = otherSet.values(); let biggerSet = values; let smallerSet = otherValues; if (otherValues.length - values.length > 0) { biggerSet = otherValues; smallerSet = values; } smallerSet.forEach(value => { if (biggerSet.includes(value)) { intersectionSet.add(value); } }); return intersectionSet; } difference(otherSet: Set<T>) { const differenceSet = new Set<T>(); this.values().forEach(value => { if (!otherSet.has(value)) { differenceSet.add(value); } }); return differenceSet; } isSubsetOf(otherSet: Set<T>) { if (this.size() > otherSet.size()) { return false; } let isSubset = true; this.values().every(value => { if (!otherSet.has(value)) { isSubset = false; return false; } return true; }); return isSubset; } isEmpty() { return this.size() === 0; } size() { return Object.keys(this.items).length; } clear() { this.items = {}; } toString() { if (this.isEmpty()) { return ''; } const values = this.values(); let objString = `${values[0]}`; for (let i = 1; i < values.length; i++) { objString = `${objString},${values[i].toString()}`; } return objString; } }
class ValuePair<K, V> { constructor(public key: K, public value: V) {} toString() { return `[#${this.key}: ${this.value}]`; } } function defaultToString(item: any): string { if (item === null) { return 'NULL'; } else if (item === undefined) { return 'UNDEFINED'; } else if (typeof item === 'string' || item instanceof String) { return `${item}`; } return item.toString(); }
class Dictionary<K, V> { private table: { [key: string]: ValuePair<K, V> }; constructor(private toStrFn: (key: K) => string = defaultToString) { this.table = {}; } set(key: K, value: V) { if (key != null && value != null) { const tableKey = this.toStrFn(key); this.table[tableKey] = new ValuePair(key, value); return true; } return false; } get(key: K): V { const valuePair = this.table[this.toStrFn(key)]; return valuePair == null ? undefined : valuePair.value; } hasKey(key: K) { return this.table[this.toStrFn(key)] != null; } remove(key: K) { if (this.hasKey(key)) { delete this.table[this.toStrFn(key)]; return true; } return false; } values(): V[] { return this.keyValues().map( (valuePair: ValuePair<K, V>) => valuePair.value ); } keys(): K[] { return this.keyValues().map( (valuePair: ValuePair<K, V>) => valuePair.key ); } keyValues(): ValuePair<K, V>[] { return Object.values(this.table); } forEach(callbackFn: (key: K, value: V) => any) { const valuePairs = this.keyValues(); for (let i = 0; i < valuePairs.length; i++) { const result = callbackFn(valuePairs[i].key, valuePairs[i].value); if (result === false) { break; } } } isEmpty() { return this.size() === 0; } size() { return Object.keys(this.table).length; } clear() { this.table = {}; } toString() { if (this.isEmpty()) { return ''; } const valuePairs = this.keyValues(); let objString = `${valuePairs[0].toString()}`; for (let i = 1; i < valuePairs.length; i++) { objString = `${objString},${valuePairs[i].toString()}`; } return objString; } }
class ValuePair<K, V> { constructor(public key: K, public value: V) {} toString() { return `[#${this.key}: ${this.value}]`; } } function defaultToString(item: any): string { if (item === null) { return 'NULL'; } else if (item === undefined) { return 'UNDEFINED'; } else if (typeof item === 'string' || item instanceof String) { return `${item}`; } return item.toString(); }
class HashTable<K, V> { protected table: { [key: string]: ValuePair<K, V> }; constructor(protected toStrFn: (key: K) => string = defaultToString) { this.table = {}; } private loseloseHashCode(key: K) { if (typeof key === 'number') { return key; } const tableKey = this.toStrFn(key); let hash = 0; for (let i = 0; i < tableKey.length; i++) { hash += tableKey.charCodeAt(i); } return hash % 37; } /* private djb2HashCode(key: K) { const tableKey = this.toStrFn(key); let hash = 5381; for (let i = 0; i < tableKey.length; i++) { hash = (hash * 33) + tableKey.charCodeAt(i); } return hash % 1013; } */ hashCode(key: K) { return this.loseloseHashCode(key); } put(key: K, value: V) { if (key != null && value != null) { const position = this.hashCode(key); this.table[position] = new ValuePair(key, value); return true; } return false; } get(key: K) { const valuePair = this.table[this.hashCode(key)]; return valuePair == null ? undefined : valuePair.value; } remove(key: K) { const hash = this.hashCode(key); const valuePair = this.table[hash]; if (valuePair != null) { delete this.table[hash]; return true; } return false; } getTable() { return this.table; } isEmpty() { return this.size() === 0; } size() { return Object.keys(this.table).length; } clear() { this.table = {}; } toString() { if (this.isEmpty()) { return ''; } const keys = Object.keys(this.table); let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; for (let i = 1; i < keys.length; i++) { objString = `${objString},{${keys[i]} => ${this.table[keys[i]].toString()}}`; } return objString; } }
class ValuePair<K, V> { constructor(public key: K, public value: V) {} toString() { return `[#${this.key}: ${this.value}]`; } } function defaultToString(item: any): string { if (item === null) { return 'NULL'; } else if (item === undefined) { return 'UNDEFINED'; } else if (typeof item === 'string' || item instanceof String) { return `${item}`; } return item.toString(); }
class HashTableSeparateChaining<K, V> { protected table: { [key: string]: LinkedList<ValuePair<K, V>> }; constructor(protected toStrFn: (key: K) => string = defaultToString) { this.table = {}; } private loseloseHashCode(key: K) { if (typeof key === 'number') { return key; } const tableKey = this.toStrFn(key); let hash = 0; for (let i = 0; i < tableKey.length; i++) { hash += tableKey.charCodeAt(i); } return hash % 37; } hashCode(key: K) { return this.loseloseHashCode(key); } put(key: K, value: V) { if (key != null && value != null) { const position = this.hashCode(key); if (this.table[position] == null) { this.table[position] = new LinkedList<ValuePair<K, V>>(); } this.table[position].push(new ValuePair(key, value)); return true; } return false; } get(key: K) { const position = this.hashCode(key); const linkedList = this.table[position]; if (linkedList != null && !linkedList.isEmpty()) { let current = linkedList.getHead(); while (current != null) { if (current.element.key === key) { return current.element.value; } current = current.next; } } return undefined; } remove(key: K) { const position = this.hashCode(key); const linkedList = this.table[position]; if (linkedList != null && !linkedList.isEmpty()) { let current = linkedList.getHead(); while (current != null) { if (current.element.key === key) { linkedList.remove(current.element); if (linkedList.isEmpty()) { delete this.table[position]; } return true; } current = current.next; } } return false; } isEmpty() { return this.size() === 0; } size() { let count = 0; Object.values(this.table).forEach(linkedList => count += linkedList.size()); return count; } clear() { this.table = {}; } getTable() { return this.table; } toString() { if (this.isEmpty()) { return ''; } const keys = Object.keys(this.table); let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; for (let i = 1; i < keys.length; i++) { objString = `${objString},{${keys[i]} => ${this.table[ keys[i] ].toString()}}`; } return objString; } }
class HashTableLinearProbing<K, V> { protected table: { [key: string]: ValuePair<K, V> }; constructor(protected toStrFn: (key: K) => string = defaultToString) { this.table = {}; } private loseloseHashCode(key: K) { if (typeof key === 'number') { return key; } const tableKey = this.toStrFn(key); let hash = 0; for (let i = 0; i < tableKey.length; i++) { hash += tableKey.charCodeAt(i); } return hash % 37; } hashCode(key: K) { return this.loseloseHashCode(key); } put(key: K, value: V) { if (key != null && value != null) { const position = this.hashCode(key); if (this.table[position] == null) { this.table[position] = new ValuePair(key, value); } else { let index = position + 1; while (this.table[index] != null) { index++; } this.table[index] = new ValuePair(key, value); } return true; } return false; } get(key: K) { const position = this.hashCode(key); if (this.table[position] != null) { if (this.table[position].key === key) { return this.table[position].value; } let index = position + 1; while (this.table[index] != null && this.table[index].key !== key) { index++; } if (this.table[index] != null && this.table[index].key === key) { return this.table[position].value; } } return undefined; } remove(key: K) { const position = this.hashCode(key); if (this.table[position] != null) { if (this.table[position].key === key) { delete this.table[position]; this.verifyRemoveSideEffect(key, position); return true; } let index = position + 1; while (this.table[index] != null && this.table[index].key !== key) { index++; } if (this.table[index] != null && this.table[index].key === key) { delete this.table[index]; this.verifyRemoveSideEffect(key, index); return true; } } return false; } private verifyRemoveSideEffect(key: K, removedPosition: number) { const hash = this.hashCode(key); let index = removedPosition + 1; while (this.table[index] != null) { const posHash = this.hashCode(this.table[index].key); if (posHash <= hash || posHash <= removedPosition) { this.table[removedPosition] = this.table[index]; delete this.table[index]; removedPosition = index; } index++; } } isEmpty() { return this.size() === 0; } size() { return Object.keys(this.table).length; } clear() { this.table = {}; } getTable() { return this.table; } toString() { if (this.isEmpty()) { return ''; } const keys = Object.keys(this.table); let objString = `{${keys[0]} => ${this.table[keys[0]].toString()}}`; for (let i = 1; i < keys.length; i++) { objString = `${objString},{${keys[i]} => ${this.table[ keys[i] ].toString()}}`; } return objString; } }
class Node<K> { left: Node<K>; right: Node<K>; constructor(public key: K) {} toString() { return `${this.key}`; } } function defaultCompare<T>(a: T, b: T): number { if (a === b) { return Compare.EQUALS; } return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; } enum Compare { LESS_THAN = -1, BIGGER_THAN = 1, EQUALS = 0 } type ICompareFunction<T> = (a: T, b: T) => number;
class BinarySearchTree<T> { protected root: Node<T>; constructor(protected compareFn: ICompareFunction<T> = defaultCompare) {} insert(key: T) { // special case: first key if (this.root == null) { this.root = new Node(key); } else { this.insertNode(this.root, key); } } protected insertNode(node: Node<T>, key: T) { if (this.compareFn(key, node.key) === Compare.LESS_THAN) { if (node.left == null) { node.left = new Node(key); } else { this.insertNode(node.left, key); } } else if (node.right == null) { node.right = new Node(key); } else { this.insertNode(node.right, key); } } getRoot() { return this.root; } search(key: T) { return this.searchNode(this.root, key); } private searchNode(node: Node<T>, key: T): boolean { if (node == null) { return false; } if (this.compareFn(key, node.key) === Compare.LESS_THAN) { return this.searchNode(node.left, key); } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { return this.searchNode(node.right, key); } // key is equal to node.item return true; } inOrderTraverse(callback: Function) { this.inOrderTraverseNode(this.root, callback); } private inOrderTraverseNode(node: Node<T>, callback: Function) { if (node != null) { this.inOrderTraverseNode(node.left, callback); callback(node.key); this.inOrderTraverseNode(node.right, callback); } } preOrderTraverse(callback: Function) { this.preOrderTraverseNode(this.root, callback); } private preOrderTraverseNode(node: Node<T>, callback: Function) { if (node != null) { callback(node.key); this.preOrderTraverseNode(node.left, callback); this.preOrderTraverseNode(node.right, callback); } } postOrderTraverse(callback: Function) { this.postOrderTraverseNode(this.root, callback); } private postOrderTraverseNode(node: Node<T>, callback: Function) { if (node != null) { this.postOrderTraverseNode(node.left, callback); this.postOrderTraverseNode(node.right, callback); callback(node.key); } } min() { return this.minNode(this.root); } protected minNode(node: Node<T>) { let current = node; while (current != null && current.left != null) { current = current.left; } return current; } max() { return this.maxNode(this.root); } protected maxNode(node: Node<T>) { let current = node; while (current != null && current.right != null) { current = current.right; } return current; } remove(key: T) { this.root = this.removeNode(this.root, key); } protected removeNode(node: Node<T>, key: T) { if (node == null) { return null; } if (this.compareFn(key, node.key) === Compare.LESS_THAN) { node.left = this.removeNode(node.left, key); return node; } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) { node.right = this.removeNode(node.right, key); return node; } else { // key is equal to node.item // handle 3 special conditions // 1 - a leaf node // 2 - a node with only 1 child // 3 - a node with 2 children // case 1 if (node.left == null && node.right == null) { node = null; return node; } // case 2 if (node.left == null) { node = node.right; return node; } else if (node.right == null) { node = node.left; return node; } // case 3 const aux = this.minNode(node.right); node.key = aux.key; node.right = this.removeNode(node.right, aux.key); return node; } } }