近期在學習數據結構的東西,基於ES6標準,寫了一些自定義結構類javascript
數據結構:Typescript版本前端
class Stack { constructor() { this.count = 0; this.items = {}; } push(element) { 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; } }
push(el or els): 添加元素,只添加到棧頂(能夠是一個或多個) pop: 移除元素,遵循棧原則,移除最後添加的元素,並返回溢出的元素 peek: 返回棧頂的元素 isEmpty: 棧是否爲空 clear: 清空棧 size: 棧的元素個數 toString: 轉換棧內容爲字符串
棧實現:字符串括號匹配java
場景:電腦打印文件,添加進入隊列的文件按前後順序打印node
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.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; } }
enqueue(el or els): 添加元素到隊列,只能向隊列尾部添加(能夠是一個或多個) dequeue: 移除隊列第一項,隊列最早放入的數據,返回被移除的元素 peek: 返回第一個添加的元素 isEmpty: 隊列是否爲空 clear: 清空隊列 size: 隊列的元素個數 toString: 輸出隊列元素轉換的字符串
場景:電影院排隊,一我的剛買了票,須要再詢問一些簡單信息,能夠直接回到隊伍頭部;在隊伍末尾的人若是不想看電影,能夠直接離開隊伍數據庫
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.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.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; } }
addFront(el): 在雙端隊列前端添加新元素 addBack(el): 在雙端隊列後端添加新元素 removeFront: 從雙端隊列前端移除第一個元素 removeBack: 從雙端隊列後端移除第一個元素 peekFront: 返回雙端隊列的前端第一個元素 peekBack: 返回雙端隊列的後端第一個元素 isEmpty: 雙端隊列是否爲空 clear: 清空雙端隊列 size: 雙端隊列元素個數 toString: 轉換爲字符串輸出
循環隊列:擊鼓傳花;雙端隊列:迴文檢查segmentfault
場景:火車,每節車都是鏈表元素,車間的連接就是指針數組
// 鏈表元素節點 Node { constructor(element, next) { this.element = element; this.next = next; } } function defaultEquals(a, b) { return a === b; }
LinkedList { constructor(equalsFn = defaultEquals) { this.equalsFn = equalsFn; this.count = 0; this.head = undefined; } push(element) { 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) { 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); 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) { 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) { const index = this.indexOf(element); return this.removeAt(index); } indexOf(element) { 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; } }
push(el): 向鏈表尾部添加元素 insert(el, position): 向鏈表特定位置添加元素 getElementAt(index): 返回鏈表特定位置元素,若是不存在,返回undefined remove: 返回鏈表移除一個元素 indexOf: 返回元素在鏈表索引,不存在元素返回-1 removeAt(position): 從鏈表特定位置刪除元素 isEmpty: 鏈表是否爲空 size: 鏈表的元素個數 toString: 轉換輸出字符串
DoublyNode extends Node { constructor(element, next, prev) { super(element, next); this.prev = prev; } }
DoublyLinkedList extends LinkedList { constructor(equalsFn = defaultEquals) { super(equalsFn); this.tail = undefined; } push(element) { 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, index) { 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; // NEW } else { node.next = this.head; this.head.prev = node; // NEW this.head = node; } } else if (index === this.count) { // last item NEW 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; // NEW node.prev = previous; // NEW } this.count++; return true; } return false; } removeAt(index) { if (index >= 0 && index < this.count) { let current = this.head; if (index === 0) { this.head = this.head.next; // 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; } } else if (index === this.count - 1) { // last item //NEW current = this.tail; 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; current.next.prev = previous; // NEW } this.count--; return current.element; } return undefined; } indexOf(element) { 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; } }
push(el): 向鏈表尾部添加元素 insert(el, position): 向鏈表特定位置添加元素 getElementAt(index): 返回鏈表特定位置元素,若是不存在,返回undefined remove: 返回鏈表移除一個元素 indexOf: 返回元素在鏈表索引,不存在元素返回-1 removeAt(position): 從鏈表特定位置刪除元素 isEmpty: 鏈表是否爲空 size: 鏈表的元素個數 toString: 轉換輸出字符串 新增方法: getHead: 返回雙鏈表第一個元素節點 getTail: 返回雙鏈表最後一個元素節點 inverseToString: 返回雙鏈表倒敘字符串
CircularLinkedList extends LinkedList { constructor(equalsFn = defaultEquals) { super(equalsFn); } push(element) { 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, index) { 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) { 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; } }
push(el): 向鏈表尾部添加元素 insert(el, position): 向鏈表特定位置添加元素 removeAt(position): 從鏈表特定位置刪除元素
const Compare = { LESS_THAN: -1, BIGGER_THAN: 1, EQUALS: 0 }; function defaultCompare(a, b) { if (a === b) { return Compare.EQUALS; } return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; }
SortedLinkedList extends LinkedList { constructor(equalsFn = defaultEquals, compareFn = defaultCompare) { super(equalsFn); this.equalsFn = equalsFn; this.compareFn = compareFn; } push(element) { if (this.isEmpty()) { super.push(element); } else { const index = this.getIndexNextSortedElement(element); super.insert(element, index); } } insert(element, index = 0) { if (this.isEmpty()) { return super.insert(element, index === 0 ? index : 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; } }
push(el): 向鏈表尾部添加元素 insert(el, position): 向鏈表特定位置添加元素 getIndexNextSortedElement(el): 返回元素在有序鏈表的位置,實際位置index+1;若是不存在,返回最後一個元素位置
場景:無序惟一數據結構
class Set { constructor() { this.items = {}; } add(element) { if (!this.has(element)) { this.items[element] = element; return true; } return false; } delete(element) { if (this.has(element)) { delete this.items[element]; return true; } return false; } has(element) { return Object.prototype.hasOwnProperty.call(this.items, element); } values() { return Object.values(this.items); } union(otherSet) { const unionSet = new Set(); this.values().forEach(value => unionSet.add(value)); otherSet.values().forEach(value => unionSet.add(value)); return unionSet; } intersection(otherSet) { const intersectionSet = new Set(); 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) { const differenceSet = new Set(); this.values().forEach(value => { if (!otherSet.has(value)) { differenceSet.add(value); } }); return differenceSet; } isSubsetOf(otherSet) { 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; } }
add(el): 向集合添加一個新元素 delete(el): 從集合移除一個元素 has(el): 集合是否包含元素 clear: 清空集合 size: 集合元素個數 values: 返回包含集合全部值的數組 isEmpty: 集合是否爲空 toString: 轉換集合值爲字符串 union(set): 並集 intersection(set): 交集 difference(set): 差集 isSubsetOf(set): 子集
並集:new Set([...setA, ...setB])
交集:new Set([...setA].filter(x => setB.has(x)))
補集:new Set([...setA].filter(x => !setBhas(x)))
ide
class ValuePair { constructor(key, value) { this.key = key; this.value = value; } toString() { return `[#${this.key}: ${this.value}]`; } } function defaultToString(item) { 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 { constructor(toStrFn = defaultToString) { this.toStrFn = toStrFn; this.table = {}; } set(key, value) { if (key != null && value != null) { const tableKey = this.toStrFn(key); this.table[tableKey] = new ValuePair(key, value); return true; } return false; } get(key) { const valuePair = this.table[this.toStrFn(key)]; return valuePair == null ? undefined : valuePair.value; } hasKey(key) { return this.table[this.toStrFn(key)] != null; } remove(key) { if (this.hasKey(key)) { delete this.table[this.toStrFn(key)]; return true; } return false; } values() { return this.keyValues().map(valuePair => valuePair.value); } keys() { return this.keyValues().map(valuePair => valuePair.key); } keyValues() { return Object.values(this.table); } forEach(callbackFn) { 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; } }
set(key, value): 向字典添加新元素,若是key存在,value會覆蓋當前已經存在的值 get(key): 獲取當前key的value remove(key): 使用key來刪除對應字典內的鍵值對 hasKey(key): 是否在字典中存在當前key對應的鍵值對 clear: 清空字典 size: 返回字典的元素個數 isEmpty: 字典是否爲空 toString: 轉換字典鍵值對爲字符串 keys: 字典全部鍵名,輸出爲數組 values: 字典全部鍵值對的值,輸出爲數組 keyValues: 字典全部的[鍵, 值]對 forEach(cb): 迭代字典全部元素,cb是回調函數,有兩個參數:key和value。在回調函數返回false停止循環
場景:數據庫建索引;javascript內部使用散列表來表示每一個對象
class ValuePair { constructor(key, value) { this.key = key; this.value = value; } toString() { return `[#${this.key}: ${this.value}]`; } } function defaultToString(item) { 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 { constructor(toStrFn = defaultToString) { this.toStrFn = toStrFn; this.table = {}; } // 建立散列表值 djb2HashCode(key) { 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) { return this.djb2HashCode(key); } put(key, value) { if (key != null && value != null) { const position = this.hashCode(key); this.table[position] = new ValuePair(key, value); return true; } return false; } get(key) { const valuePair = this.table[this.hashCode(key)]; return valuePair == null ? undefined : valuePair.value; } remove(key) { 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; } }
put(key, value): 向散列表增長元素 remove(key): 鍵值在散列表中移除值 get(key): 鍵值檢索特定的值 loseloseHashCode(key): 散列函數 hashCode(key): 返回鍵名對應的鍵值對數據 getTable: 返回散列表 isEmpty: 散列表是否爲空 size: 散列表元素個數 clear: 清空散列表 toString: 轉換鍵值對爲字符串
通常散列表對於重複的生成散列值會覆蓋數據,也就是返回的是相同散列值最後添加那個元素。分離連接在每一個散列值位置建立一個鏈表來儲存元素,不會覆蓋
class ValuePair { constructor(key, value) { this.key = key; this.value = value; } toString() { return `[#${this.key}: ${this.value}]`; } } function defaultToString(item) { 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 { constructor(toStrFn = defaultToString) { this.toStrFn = toStrFn; this.table = {}; } // 建立散列表值 djb2HashCode(key) { 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) { return this.djb2HashCode(key); } put(key, value) { if (key != null && value != null) { const position = this.hashCode(key); if (this.table[position] == null) { // 單向鏈表 this.table[position] = new LinkedList(); } this.table[position].push(new ValuePair(key, value)); return true; } return false; } get(key) { 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) { 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; } }
方法使用同通常散列表方法,重寫了put、get、remove方法
也是爲了處理通常散列表覆蓋值的問題,線性是由於不建立額外的數據結構,直接儲存在表中
class HashTableLinearProbing { constructor(toStrFn = defaultToString) { this.toStrFn = toStrFn; this.table = {}; } // 建立散列表值 djb2HashCode(key) { 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) { return this.djb2HashCode(key); } put(key, value) { 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) { 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) { 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; } verifyRemoveSideEffect(key, removedPosition) { 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; } }
使用方法同通常散列表方法
原生Map使用方式:
const map = new Map(); map.set('whh', 'whh@163.com'); map.set('lsd', 'lsd@163.com'); map.set('lbb', 'lbb@163.com'); console.log(map.has('whh')); // true console.log(map.size); // 3 console.log(map.keys()); // {"whh", "lsd", "lbb"} console.log(map.values()); // {"whh@163.com", "lsd@163.com", "lbb@163.com"} console.log(map.get('lsd')); // "lsd@163.com"
Map類的values和keys方法返回的是Iterator,不是鍵值對構成的數組。size是屬性,不是方法,刪除元素用delete(key)
這兩個類是Map和Set的弱化版本:
一、這兩個類沒有entries、keys和values等方法; 二、只能用對象做爲鍵; 三、建立這兩個類主要是爲了性能,沒有強引用鍵,使得js的垃圾回收能夠直接清除入口; 四、沒有entries、keys和values等方法,實例不知道原始類內部狀況,能夠用來封裝類的私有屬性
一種特殊的操做樹和圖結構的方法,遞歸迭代
遞歸引發的棧溢出問題
遞歸引發的重複計算問題
非順序數據結構,父級節點下能夠有多個或者零個子節點
父級節點下最多兩個子節點
class Node { constructor(key) { this.key = key; this.left = null; this.right = null; } } function defaultCompare(a, b) { if (a === b) { return Compare.EQUALS; } return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; }
const Compare = { LESS_THAN: -1, BIGGER_THAN: 1, EQUALS: 0 }; class BinarySearchTree { constructor(compareFn = defaultCompare) { this.compareFn = compareFn; this.root = undefined; } insert(key) { // special case: first key if (this.root == null) { this.root = new Node(key); } else { this.insertNode(this.root, key); } } insertNode(node, key) { 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) { return this.searchNode(this.root, key); } searchNode(node, key) { 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); } return true; } inOrderTraverse(callback) { this.inOrderTraverseNode(this.root, callback); } inOrderTraverseNode(node, callback) { if (node != null) { this.inOrderTraverseNode(node.left, callback); callback(node.key); this.inOrderTraverseNode(node.right, callback); } } preOrderTraverse(callback) { this.preOrderTraverseNode(this.root, callback); } preOrderTraverseNode(node, callback) { if (node != null) { callback(node.key); this.preOrderTraverseNode(node.left, callback); this.preOrderTraverseNode(node.right, callback); } } postOrderTraverse(callback) { this.postOrderTraverseNode(this.root, callback); } postOrderTraverseNode(node, callback) { if (node != null) { this.postOrderTraverseNode(node.left, callback); this.postOrderTraverseNode(node.right, callback); callback(node.key); } } min() { return this.minNode(this.root); } minNode(node) { let current = node; while (current != null && current.left != null) { current = current.left; } return current; } max() { return this.maxNode(this.root); } maxNode(node) { let current = node; while (current != null && current.right != null) { current = current.right; } return current; } remove(key) { this.root = this.removeNode(this.root, key); } removeNode(node, key) { if (node == null) { return undefined; } 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; } // 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 = undefined; 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; } }
insert(key):插入新的鍵 getRoot:返回根節點 search(key):查找鍵,返回true和false inOrderTraverse:中序遍歷節點 perOrderTraverse:先序遍歷節點 postOrderTraverse:後序遍歷節點 min:樹中最小的鍵 max:樹中最大的鍵 remove(key):刪除鍵