diff --git a/problem-1/sortLSD.test.js b/problem-1/sortLSD.test.js index 0299913..e1eff09 100644 --- a/problem-1/sortLSD.test.js +++ b/problem-1/sortLSD.test.js @@ -1,4 +1,32 @@ const sortLSD = (words, w = words.length) => { + const N = words.length; + const R = 256; + const aux = []; + + for (let d = w - 1; d >= 0; d--) { + const count = new Array(R + 1).fill(0); + + // 빈도수 + // words[i].charCodeAt(d) 현재 단어 위치 + for (let i = 0; i < N; i++) { + count[words[i].charCodeAt(d) + 1]++; + } + + // 처음 등장 인덱스 + for (let i = 0; i < R; i++) { + count[i + 1] += count[i]; + } + + // 임시 배열 분배 + for (let i = 0; i < N; i++) { + aux[count[words[i].charCodeAt(d)]++] = words[i]; + } + + // 원본 수정 + for (let i = 0; i < N; i++) { + words[i] = aux[i]; + } + } }; test('sortLSD는 문자열을 정렬한다.', () => { diff --git a/problem-1/sortMSD.test.js b/problem-1/sortMSD.test.js index b3bef71..297978a 100644 --- a/problem-1/sortMSD.test.js +++ b/problem-1/sortMSD.test.js @@ -34,6 +34,37 @@ const CUTOFF = 15; const charAt = (s, d) => (d < s.length ? s.charCodeAt(d) : -1); const sort = (words, aux, lo, hi, d) => { + // 정렬 범위 + if (hi <= lo + CUTOFF) { + insertionSort(words, lo, hi, d); + return; + } + + const count = new Array(R + 2).fill(0); + + // 빈도수 + for (let i = lo; i <= hi; i++) { + count[charAt(words[i], d) + 2]++; + } + + // 시작 인덱스 + for (let i = 0; i < R + 1; i++) { + count[i + 1] += count[i]; + } + + // 임시 배열 분해 + for (let i = lo; i <= hi; i++) { + aux[count[charAt(words[i], d) + 1]++] = words[i]; + } + + // 원본 수정 + for (let i = lo; i <= hi; i++) { + words[i] = aux[i - lo]; + } + + for (let i = 0; i < R; i++) { + sort(words, aux, lo + count[i], lo + count[i + 1] - 1, d + 1); + } }; const sortMSD = (words) => { diff --git a/problem-1/sortQuickThree.test.js b/problem-1/sortQuickThree.test.js index 76ae3f8..c0b351c 100644 --- a/problem-1/sortQuickThree.test.js +++ b/problem-1/sortQuickThree.test.js @@ -5,6 +5,32 @@ const exchange = (words, i, j) => { }; const sortQuickThree = (words, lo = 0, hi = words.length - 1, d = 0) => { + if (hi <= lo) { + return; + } + + const pivot = charAt(words[lo], d); + + let lt = lo; + let gt = hi; + let i = lo + 1; + + while (i <= gt) { + const t = charAt(words[i], d); + if (t < pivot) { + exchange(words, lt++, i++); + } else if (t > pivot) { + exchange(words, i, gt--); + } else { + i++; + } + } + + sortQuickThree(words, lo, lt - 1, d); + if (pivot >= 0) { + sortQuickThree(words, lt, gt, d + 1); + } + sortQuickThree(words, gt + 1, hi, d); }; test('sortQuickThree는 문자열을 정렬한다.', () => { diff --git a/problem-2/Trie.test.js b/problem-2/Trie.test.js index 771d8de..a06223f 100644 --- a/problem-2/Trie.test.js +++ b/problem-2/Trie.test.js @@ -10,12 +10,57 @@ class Trie { #root = new Node(); get(key) { + return this.#get(this.#root, key, 0)?.value; + } + + #get(node, key, d) { + if (!node) { + return; + } + + if (d === key.length) { + return node; + } + + const c = key.charCodeAt(d); + return this.#get(node.next[c], key, d + 1); } put(key, value) { + this.#root = this.#put(this.#root, key, value, 0); + } + + #put(node, key, value, d) { + if (!node) { + node = new Node(); + } + + if (d === key.length) { + node.value = value; + return node; + } + + const c = key.charCodeAt(d); + node.next[c] = this.#put(node.next[c], key, value, d + 1); + return node; } size() { + return this.#size(this.#root); + } + + #size(node) { + if (node === undefined) { + return 0; + } + + let count = node.value !== undefined ? 1 : 0; + + for (let i = 0; i < R; i++) { + count += this.#size(node.next[i]); + } + + return count; } keys() { @@ -23,15 +68,108 @@ class Trie { } keysWithPrefix(prefix) { + const queue = []; + this.#collect(this.#get(this.#root, prefix, 0), prefix, queue); + return queue; + } + + #collect(node, prefix, queue) { + if (node === undefined) { + return; + } + + if (node.value !== undefined) { + queue.push(prefix); + } + + for (let i = 0; i < R; i++) { + this.#collect(node.next[i], prefix + String.fromCharCode(i), queue); + } } keysThatMatch(pattern) { + const queue = []; + this.#collectWithPattern(this.#root, '', pattern, queue); + return queue; + } + + #collectWithPattern(node, prefix, pattern, queue) { + if (node === undefined) { + return; + } + + if (node === undefined) { + return; + } + + if (prefix.length === pattern.length && node.value !== undefined) { + queue.push(prefix); + } + + if (prefix.length === pattern.length) { + return; + } + + const next = pattern[prefix.length]; + for (let i = 0; i < R; i++) { + if (next === '.' || next === String.fromCharCode(i)) { + this.#collectWithPattern( + node.next[i], + prefix + String.fromCharCode(i), + pattern, + queue, + ); + } + } } delete(key) { + this.#root = this.#delete(this.#root, key, 0); + } + + #delete(node, key, d) { + if (node === undefined) { + return; + } + + if (d === key.length) { + node.value = undefined; + } else { + const c = key.charCodeAt(d); + node.next[c] = this.#delete(node.next[c], key, d + 1); + } + + if (node.value !== undefined) { + return node; + } + + for (let i = 0; i < R; i++) { + if (node.next[i] !== undefined) { + return node; + } + } } longestPrefixOf(query) { + const length = this.#search(this.#root, query, 0, 0); + return query.substring(0, length); + } + + #search(node, query, d, length) { + if (node === undefined) { + return length; + } + + if (node.value !== undefined) { + length = d; + } + + if (d === query.length) { + return length; + } + + const c = query.charCodeAt(d); + return this.#search(node.next[c], query, d + 1, length); } } diff --git a/problem-3/repeat-length-encoding.test.js b/problem-3/repeat-length-encoding.test.js index 42b52ab..77c28c7 100644 --- a/problem-3/repeat-length-encoding.test.js +++ b/problem-3/repeat-length-encoding.test.js @@ -1,11 +1,26 @@ const encode = (words) => { + const answer = []; + + for (const input of words) { + answer.push(input[0] + input.length); + } + + return answer.join(''); }; const decode = (string) => { + const answer = []; + + for (let i = 0; i < string.length - 1; i += 2) { + const count = Number(string[i + 1]); + answer.push(string[i].repeat(count)); + } + + return answer; }; test('repeat length encoding', () => { - const origin = ['a', 'aa', 'aaa', 'aaaa', 'aaaaa', 'aaaaaa', 'aaaaaaaa', 'aaaaaaaa']; + const origin = ['a', 'aa', 'aaa', 'aaaa', 'aaaaa', 'aaaaaa', 'aaaaaaa', 'aaaaaaaa']; const encodedString = encode(origin);