diff --git a/problem-1/SymbolTableWithArray.js b/problem-1/SymbolTableWithArray.js index 761c9ef..80fa60b 100644 --- a/problem-1/SymbolTableWithArray.js +++ b/problem-1/SymbolTableWithArray.js @@ -1,4 +1,92 @@ +class Node { + key; + + item; + + constructor(key, item) { + this.key = key; + this.item = item; + } +} + class SymbolTable { + #items = []; + + #n = 0; + + size() { + return this.#n; + } + + isEmpty() { + return this.size() === 0; + } + + #findIndex(key) { + return this.#items.findIndex((item) => item.key === key); + } + + get(key) { + return this.#items.find((item) => item.key === key)?.item; + } + + put(key, item) { + const curIndex = this.#findIndex(key); + if (curIndex >= 0) { + this.#items[curIndex].item = item; + + return; + } + + const node = new Node(key, item); + + this.#items[this.#n] = node; + + this.#n += 1; + } + + delete(key) { + const index = this.#findIndex(key); + + if (index < 0) { + return; + } + + this.#items.splice(index, 1); + + this.#n -= 1; + } + + contains(key) { + return !!this.get(key); + } + + values() { + return this.#items.map(({ item }) => item); + } + + keys() { + const data = [...this.#items]; + let index = 0; + + return { + [Symbol.iterator]() { + return { + next() { + if (index >= data.length) { + return { done: true }; + } + + const { key } = data[index]; + + index += 1; + + return { done: false, value: key }; + }, + }; + }, + }; + } } module.exports = { diff --git a/problem-1/problem-1.test.js b/problem-1/problem-1.test.js index 9f38cf6..a8fedbe 100644 --- a/problem-1/problem-1.test.js +++ b/problem-1/problem-1.test.js @@ -124,3 +124,19 @@ test('심볼 테이블은 키에 해당하는 값을 저장한다', () => { expect(st.get('something')).toBeUndefined(); expect(st.get('this')).toBeUndefined(); }); + +test('키를 순회할 수 있다', () => { + const st = new SymbolTable(); + + st.put('foo', 'bar'); + st.put('something', 'that'); + st.put('this', 'is'); + + const keys = []; + + for (const key of st.keys()) { + keys.push(key); + } + + expect(keys).toEqual(['foo', 'something', 'this']); +}); diff --git a/problem-2/SymbolTable.js b/problem-2/SymbolTable.js index 6bb3c68..c3eecab 100644 --- a/problem-2/SymbolTable.js +++ b/problem-2/SymbolTable.js @@ -63,18 +63,28 @@ class SymbolTable { this.#n--; } - rank(key, start = 0, end = this.#n - 1) { - if (start > end) { - return start; + rank(key) { + let mid = 0; + let start = 0; + let end = this.#n - 1; + + while (start <= end) { + // 가운데 인덱스 + mid = Math.floor((start + end) / 2); + + if (this.#keys[mid] === key) { + return mid; + } + + // 대소 비교로 범위 지정 + if (this.#keys[mid] > key) { + end = mid - 1; + } else { + start = mid + 1; + } } - const mid = start + Math.floor((end - start) / 2); - if (key < this.#keys[mid]) { - return this.rank(key, start, mid - 1); - } if (key > this.#keys[mid]) { - return this.rank(key, mid + 1, end); - } - return mid; + return start; } min() { @@ -106,15 +116,54 @@ class SymbolTable { } contains(key) { + const i = this.rank(key); + + if (key === this.#keys[i] && i < this.#n) { + return true; + } + + return false; } floor(key) { + const i = this.rank(key); + + if (key === this.#keys[i]) { + return key; + } + + if (i === 0) { + return; + } + + return this.#keys[i - 1]; } ceiling(key) { + const i = this.rank(key); + + if (i >= this.#n) { + return; + } + + return this.#keys[i]; } keysRange(start, end) { + const startIndex = this.rank(this.ceiling(start)); + const endIndex = this.rank(this.floor(end)); + + if (startIndex > endIndex || startIndex < 0 || endIndex >= this.#n) { + return []; + } + + const keys = []; + + for (let k = startIndex; k <= endIndex; k++) { + keys.push(this.#keys[k]); + } + + return keys; } } diff --git a/problem-3/SymbolTable.js b/problem-3/SymbolTable.js index 8bff19c..ee77f09 100644 --- a/problem-3/SymbolTable.js +++ b/problem-3/SymbolTable.js @@ -9,10 +9,13 @@ class Node { n; - constructor(key, value, n) { + h; + + constructor(key, value, n, h = 0) { this.key = key; this.value = value; this.n = n; + this.h = h; } } @@ -23,10 +26,12 @@ class SymbolTable { return this.#root === undefined; } + // 테이블의 사이즈 반환 = 모든 노드의 개수 size() { return this.#size(this.#root); } + // eslint-disable-next-line class-methods-use-this #size(node) { if (node === undefined) { return 0; @@ -58,7 +63,7 @@ class SymbolTable { #put(node, key, value) { if (node === undefined) { - return new Node(key, value, 1); + return new Node(key, value, 1, 0); } if (key < node.key) { @@ -69,7 +74,8 @@ class SymbolTable { node.value = value; } - node.n = this.#size(node.left) + this.#size(node.right) + 1; + node.n = 1 + this.#size(node.left) + this.#size(node.right); + node.h = 1 + Math.max(this.#height(node.left), this.#height(node.right)); return node; } @@ -160,9 +166,12 @@ class SymbolTable { if (key < node.key) { return this.#rank(node.left, key); - } if (key > node.key) { + } + + if (key > node.key) { return 1 + this.#size(node.left) + this.#rank(node.right, key); } + return this.#size(node.left); } @@ -212,7 +221,8 @@ class SymbolTable { node.left = t.left; } - node.n = this.#size(node.left) + this.#size(node.right) + 1; + node.n = 1 + this.#size(node.left) + this.#size(node.right); + node.h = 1 + Math.max(this.#height(node.left), this.#height(node.right)); return node; } @@ -269,6 +279,45 @@ class SymbolTable { return this.#max(node.right); } + + height() { + if (this.isEmpty()) { + return -1; + } + + return this.#height(this.#root); + } + + // 재귀적인 방법으로 트리의 높이 구하기 1 + // #height(node, h = 0) { + // if (node === undefined) { + // return h - 1; + // } + + // return Math.max(this.#height(node.left, 1 + h), this.#height(node.right, 1 + h)); + // } + + // 재귀적인 방법으로 트리의 높이 구하기 2 + // #height(node) { + // if (!node) { + // return 0; + // } + + // if (!node.left && !node.right) { + // return 0; + // } + + // return 1 + Math.max(this.#height(node.left), this.#height(node.right)); + // } + + // eslint-disable-next-line class-methods-use-this + #height(node) { + if (node === undefined) { + return -1; + } + + return node.h; + } } module.exports = { diff --git a/problem-5/SeperateChainingHashTable.test.js b/problem-5/SeperateChainingHashTable.test.js index 6f9e2b1..11022ce 100644 --- a/problem-5/SeperateChainingHashTable.test.js +++ b/problem-5/SeperateChainingHashTable.test.js @@ -1,8 +1,18 @@ +const { SymbolTableWithLinkedList } = require('../problem-1/SymbolTableWithLinkedList'); + class SeperateChainingHashTable { #M; + #table; + constructor(maxCount = 997) { this.#M = maxCount; + + this.#table = new Array(maxCount); + + for (let i = 0; i < maxCount; i++) { + this.#table[i] = new SymbolTableWithLinkedList(); + } } #hash(key) { @@ -14,22 +24,43 @@ class SeperateChainingHashTable { } hash(key) { - return this.#hash(key) + return this.#hash(key); } get(key) { + const index = this.hash(key); + + return this.#table[index].get(key); } put(key, value) { + const index = this.hash(key); + + return this.#table[index].put(key, value); } - delete(key, value) { + delete(key) { + const index = this.hash(key); + + return this.#table[index].delete(key); } - + contains(key) { + return !!this.get(key); } keys() { + const result = []; + + this.#table.forEach((table) => { + const keys = table.keys(); + + for (const key of keys) { + result.push(key); + } + }); + + return result; } } @@ -45,7 +76,7 @@ const randomString = (max) => { } return result; -} +}; test('이미 있는 키 값에 값을 추가하면 이전 값을 덮어쓴다', () => { const st = new SeperateChainingHashTable();