diff --git a/problem-1/SymbolTableWithArray.js b/problem-1/SymbolTableWithArray.js index 761c9ef..bd6ca40 100644 --- a/problem-1/SymbolTableWithArray.js +++ b/problem-1/SymbolTableWithArray.js @@ -1,4 +1,76 @@ class SymbolTable { + #keys = []; + + #values = []; + + #n = 0; + + constructor(max = 10) { + this.#keys = new Array(max); + this.#values = new Array(max); + } + + get(key) { + for (let i = 0; i < this.#n; i++) { + if (this.#keys[i] === key) { + return this.#values[i]; + } + } + } + + put(key, value) { + for (let i = 0; i < this.#n; i++) { + if (this.#keys[i] === key) { + this.#values[i] = value; + return; + } + } + + this.#keys[this.#n] = key; + this.#values[this.#n] = value; + this.#n++; + } + + delete(key) { + for (let i = 0; i < this.#n; i++) { + if (this.#keys[i] === key) { + this.#values[i] = null; + + for (let j = i; j < this.#n - 1; j++) { + this.#keys[j] = this.#keys[j + 1]; + this.#values[j] = this.#values[j + 1]; + } + + this.#n--; + return; + } + } + } + + contains(key) { + if (this.get(key)) { + return true; + } + return false; + } + + isEmpty() { + if (this.#n === 0) { + return true; + } return false; + } + + size() { + return this.#n; + } + + keys() { + return this.#keys; + } + + values() { + return this.#values; + } } module.exports = { diff --git a/problem-2/SymbolTable.js b/problem-2/SymbolTable.js index 6bb3c68..62ea4ea 100644 --- a/problem-2/SymbolTable.js +++ b/problem-2/SymbolTable.js @@ -106,15 +106,44 @@ class SymbolTable { } contains(key) { + if (this.get(key)) { + return true; + } return false; } floor(key) { + const i = this.rank(key); + if (i === 0) { + return this.#keys[i] === key ? key : undefined; + } + + if (this.#keys[i] === key) { + return key; + } + + return this.#keys[i - 1]; } ceiling(key) { + const i = this.rank(key); + return this.#keys[i]; } keysRange(start, end) { + const startIndex = this.rank(start); + const endIndex = this.rank(end); + + const arr = []; + + for (let i = startIndex; i < endIndex; i++) { + arr.push(this.#keys[i]); + } + + if (this.#keys[endIndex] === end) { + arr.push(end); + } + + return arr; } } diff --git a/problem-3/SymbolTable.js b/problem-3/SymbolTable.js index 8bff19c..37eca99 100644 --- a/problem-3/SymbolTable.js +++ b/problem-3/SymbolTable.js @@ -9,10 +9,13 @@ class Node { n; - constructor(key, value, n) { + height; + + constructor(key, value, n, height = 0) { this.key = key; this.value = value; this.n = n; + this.height = height; } } @@ -70,6 +73,7 @@ class SymbolTable { } node.n = this.#size(node.left) + this.#size(node.right) + 1; + node.height = Math.max(this.#height(node.left), this.#height(node.right)) + 1; return node; } @@ -181,6 +185,7 @@ class SymbolTable { node.left = this.#deleteMin(node.left); node.n = this.#size(node.left) + this.#size(node.right) + 1; + node.height = Math.max(this.#height(node.left), this.#height(node.right)) + 1; return node; } @@ -210,6 +215,7 @@ class SymbolTable { node = this.min(t.right); node.right = this.#deleteMin(t.right); node.left = t.left; + node.height = Math.max(this.#height(node.left), this.#height(node.right)) + 1; } node.n = this.#size(node.left) + this.#size(node.right) + 1; @@ -269,6 +275,18 @@ class SymbolTable { return this.#max(node.right); } + + height() { + return this.#height(this.#root); + } + + #height(node) { + if (node === undefined) { + return -1; + } + + return node.height; + } } module.exports = { diff --git a/problem-5/LinearProbingHashTable.test.js b/problem-5/LinearProbingHashTable.test.js index e4a1b2b..91055b9 100644 --- a/problem-5/LinearProbingHashTable.test.js +++ b/problem-5/LinearProbingHashTable.test.js @@ -1,7 +1,10 @@ class LinearProbingHashTable { #N = 0; + #M = 0; + #keys = []; + #values = []; constructor(maxCount = 16) { @@ -19,25 +22,95 @@ class LinearProbingHashTable { } hash(key) { - return this.#hash(key) + return this.#hash(key); } get(key) { + for (let i = this.#hash(key); this.#keys[i] !== undefined; i = (i + 1) % this.#M) { + if (this.#keys[i] === key) { + return this.#values[i]; + } + } } put(key, value) { + let i = this.#hash(key); + while (true) { + if (this.#keys[i] === key) { + this.#values[i] = value; + return; + } + + if (this.#keys[i] === undefined) { + this.#N++; + this.#keys[i] = key; + this.#values[i] = value; + + if (this.#N >= this.#M / 2) { + this.#resize(this.#M * 2); + } + + return; + } + + i = (i + 1) % this.#M; + } } delete(key) { + for (let i = this.#hash(key); this.#keys[i] !== undefined; i = (i + 1) % this.#M) { + if (this.#keys[i] === key) { + // 찾았다. + this.#keys[i] = undefined; + this.#values[i] = undefined; + this.#N--; + + i = (i + 1) % this.#M; + while (this.#keys[i] !== undefined) { + const beforeKey = this.#keys[i]; + const beforeValuse = this.#values[i]; + + this.#keys[i] = undefined; + this.#values[i] = undefined; + this.#N--; + if (this.#N <= this.#M / 2) { + this.#resize(this.#M / 2); + } + this.put(beforeKey, beforeValuse); + i = (i + 1) % this.#M; + } + return; + } + } } contains(key) { + return this.get(key) !== undefined; } keys() { + return this.#keys.filter((key) => key !== undefined); } + // 크기를 2배로 키운다. #resize(capacity) { + // 이전 배열의 크기의 2배인 새로운 배열 하나 만든다. + // 이전에 배열을 꺼내서 다시 새로운 배열에 넣는다. + + const newTable = new LinearProbingHashTable(capacity); + + for (let i = 0; i < this.#M; i++) { + const key = this.#keys[i]; + const value = this.#values[i]; + + if (key !== undefined) { + newTable.put(key, value); + } + } + + this.#keys = newTable.#keys; + this.#values = newTable.#values; + this.#M = capacity; } } @@ -53,7 +126,7 @@ const randomString = (max) => { } return result; -} +}; test('이미 있는 키 값에 값을 추가하면 이전 값을 덮어쓴다', () => { const st = new LinearProbingHashTable(); diff --git a/problem-5/SeperateChainingHashTable.test.js b/problem-5/SeperateChainingHashTable.test.js index 6f9e2b1..724aea1 100644 --- a/problem-5/SeperateChainingHashTable.test.js +++ b/problem-5/SeperateChainingHashTable.test.js @@ -1,8 +1,128 @@ +class Node { + key; + + item; + + next; + + constructor(key, item, next) { + this.key = key; + this.item = item; + this.next = next; + } +} + +class SymbolTableWithLinkedList { + #first; + + #size; + + constructor() { + this.#size = 0; + } + + get(key) { + for (let i = this.#first; i !== undefined; i = i.next) { + if (i.key === key) { + return i.item; + } + } + } + + put(key, value) { + for (let i = this.#first; i !== undefined; i = i.next) { + if (i.key === key) { + i.item = value; + return; + } + } + + this.#first = new Node(key, value, this.#first); + this.#size++; + } + + delete(key) { + let prev; + + for (let curr = this.#first; curr !== undefined; curr = curr.next) { + if (curr.key === key) { + if (prev) { + prev.next = curr.next; + } else { + this.#first = curr.next; + } + + this.#size--; + return; + } + + prev = curr; + } + } + + contains(key) { + for (let i = this.#first; i !== undefined; i = i.next) { + if (i.key === key) { + return true; + } + } + + return false; + } + + isEmpty() { + return this.#size === 0; + } + + size() { + return this.#size; + } + + keys() { + let current = this.#first; + return { + [Symbol.iterator]() { + return { + next() { + if (current === undefined) { + return { done: true }; + } + + const value = current.key; + + current = current.next; + + return { done: false, value }; + }, + }; + }, + }; + } + + values() { + const array = []; + + for (let i = this.#first; i !== undefined; i = i.next) { + array.push(i.item); + } + + return array; + } +} + class SeperateChainingHashTable { #M; + #st; + constructor(maxCount = 997) { this.#M = maxCount; + + this.#st = new Array(maxCount); + + for (let i = 0; i < this.#M; i++) { + this.#st[i] = new SymbolTableWithLinkedList(); + } } #hash(key) { @@ -14,22 +134,37 @@ class SeperateChainingHashTable { } hash(key) { - return this.#hash(key) + return this.#hash(key); } get(key) { + return this.#st[this.#hash(key)].get(key); } put(key, value) { + this.#st[this.#hash(key)].put(key, value); } delete(key, value) { + this.#st[this.#hash(key)].delete(key); } - + contains(key) { + return this.get(key) !== undefined; } keys() { + const result = []; + + this.#st.forEach((table) => { + const keys = table.keys(); + + for (const key of keys) { + result.push(key); + } + }); + + return result; } } @@ -45,7 +180,7 @@ const randomString = (max) => { } return result; -} +}; test('이미 있는 키 값에 값을 추가하면 이전 값을 덮어쓴다', () => { const st = new SeperateChainingHashTable();