From 32fc22f3e24fa067aebe119d9bbea8d129690dd8 Mon Sep 17 00:00:00 2001 From: mmyeon Date: Fri, 10 Jan 2025 10:50:05 +0900 Subject: [PATCH 1/6] add solution : 20. Valid Parentheses --- valid-parentheses/mmyeon.ts | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 valid-parentheses/mmyeon.ts diff --git a/valid-parentheses/mmyeon.ts b/valid-parentheses/mmyeon.ts new file mode 100644 index 000000000..7d403c208 --- /dev/null +++ b/valid-parentheses/mmyeon.ts @@ -0,0 +1,38 @@ +/** + *@link https://leetcode.com/problems/valid-parentheses/description/ + * + * 접근 방법 : + * - 닫힌 괄호 나왔을 때 열린 괄호의 마지막 값과 짝이 맞는지 찾아야되니까 stack 사용 + * + * 시간복잡도 : O(n) + * - 문자열 순회하면서 괄호체크하니까 O(n) + * + * 공간복잡도 : O(k) + * - pairs 객체 고정된 크기로 저장 O(1) + * - stack에 열린 괄호 개수만큼 담기니까 O(k) + */ + +function isValid(s: string): boolean { + const pairs: Record = { + ")": "(", + "}": "{", + "]": "[", + }; + const stack: string[] = []; + + for (const char of s) { + // 닫는 괄호 나온 경우 처리 + if (pairs[char]) { + // 짝이 맞지 않는 경우 + if (stack[stack.length - 1] !== pairs[char]) return false; + + // 짝이 맞는 경우 + stack.pop(); + } else { + // 열린 괄호는 바로 stack에 저장 + stack.push(char); + } + } + + return stack.length === 0; +} From 17451c319101b9bb802fb7f87d4dd1ad28f22f63 Mon Sep 17 00:00:00 2001 From: mmyeon Date: Tue, 14 Jan 2025 13:11:30 +0900 Subject: [PATCH 2/6] add solution : 211. Design Add and Search Words Data Structure --- .../mmyeon.ts | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 design-add-and-search-words-data-structure/mmyeon.ts diff --git a/design-add-and-search-words-data-structure/mmyeon.ts b/design-add-and-search-words-data-structure/mmyeon.ts new file mode 100644 index 000000000..010a77fc8 --- /dev/null +++ b/design-add-and-search-words-data-structure/mmyeon.ts @@ -0,0 +1,123 @@ +/** + *@link https://leetcode.com/problems/design-add-and-search-words-data-structure/ + * + * 접근 방법 : + * - set 활용하는 방법 + * - 단어 추가할 때는 set에 저장하고, 단어 찾을 때마다 cache목록에서 값 가져오기. + * - 단어 추가하면 이전 캐시는 사용하지 못하니까 클리어 해주기 + * - 이전 값 활용한다는 장점이 있지만 단어 추가시 다시 캐시 만들어야하는 비효율 존재 + * + * 시간복잡도 : O(n * k) + * - n은 set에 추가된 단어 길이, k는 각 단어 평균 길이 + * - this.set 단어 순회 => O(n) + * - replaceAll, filter => O(k) + * + * 공간복잡도 : O(n * k) + * - n은 패턴의 개수, k는 패턴에 매칭되는 평균 단어 수 + */ +class WordDictionary { + set: Set; + cache: Map; + constructor() { + this.set = new Set(); + this.cache = new Map(); + } + + addWord(word: string): void { + this.set.add(word); + // 새로운 단어가 추가 시 캐시 클리어 + this.cache.clear(); + } + + search(word: string): boolean { + // 캐시에 값이 존재하면 즉시 리턴 + if (this.cache.has(word)) return this.cache.get(word)!.length > 0; + + // 정규표현식 패턴 생성 + const pattern = new RegExp(`^${word.replaceAll(".", "[a-z]")}$`); + const matches = [...this.set].filter((item) => pattern.test(item)); + + // 검색 결과 캐시에 저장 + this.cache.set(word, matches); + + return matches.length > 0; + } +} + +/** + * Your WordDictionary object will be instantiated and called as such: + * var obj = new WordDictionary() + * obj.addWord(word) + * var param_2 = obj.search(word) + */ + +// 시간 복잡도 개선하기 위해서 Trie 자료 구조 사용 +class TrieNode { + children: Map; + isEndOfWord: boolean; + + constructor() { + this.children = new Map(); + this.isEndOfWord = false; + } +} + +/* + * 접근 방법 : + * - 각 문자 저장하고 검색하기 위해서 Trie 구조 사용 + * - 와일드카드(.)가 포함된 단어는 모든 자식 노드 탐색하기 위해서 재귀적으로 확인 + * + * 시간복잡도 : O(n * k) + * - n : 단어 개수, m : 단어 평균 길이 => addWord의 복잡도 : O(n * m) + * - c : 자식노드 개수, d : 와일드카드 개수, m : 단어 길이 => search의 복잡도 : O(c^d * m) + * - 와일드 카드 있는 경우 각 노드 별 모든 자식 노드를 탐색해야 한다. + * + * 공간복잡도 : O(n * k) + * - n은 패턴의 개수, k는 패턴에 매칭되는 평균 단어 수 + */ +class WordDictionary { + root: TrieNode; + constructor() { + this.root = new TrieNode(); + } + + addWord(word: string): void { + let currentNode = this.root; + + for (const letter of word) { + if (!currentNode.children.has(letter)) + currentNode.children.set(letter, new TrieNode()); + currentNode = currentNode.children.get(letter)!; + } + + currentNode.isEndOfWord = true; + } + + search(word: string): boolean { + const dfs = (currentNode: TrieNode, index: number): boolean => { + if (index === word.length) return currentNode.isEndOfWord; + + const char = word[index]; + // 와일드 카드인 경우 모든 자식 노드 순회한다 + if (char === ".") { + // 모든 자식 노드 순회 + for (const key of currentNode.children.keys()) { + if (dfs(currentNode.children.get(key)!, index + 1)) return true; + } + return false; + } else { + if (!currentNode.children.has(char)) return false; + return dfs(currentNode.children.get(char)!, index + 1); + } + }; + + return dfs(this.root, 0); + } +} + +/** + * Your WordDictionary object will be instantiated and called as such: + * var obj = new WordDictionary() + * obj.addWord(word) + * var param_2 = obj.search(word) + */ From d08bcbef32cc22c00968aed5e4f8ecee3eff3a9b Mon Sep 17 00:00:00 2001 From: mmyeon Date: Tue, 14 Jan 2025 14:22:19 +0900 Subject: [PATCH 3/6] add solution : 11. Container With Most Water --- container-with-most-water/mmyeon.ts | 33 +++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 container-with-most-water/mmyeon.ts diff --git a/container-with-most-water/mmyeon.ts b/container-with-most-water/mmyeon.ts new file mode 100644 index 000000000..14ea25d5a --- /dev/null +++ b/container-with-most-water/mmyeon.ts @@ -0,0 +1,33 @@ +/** + *@link https://leetcode.com/problems/container-with-most-water/description/ + * + * 접근 방법 : + * - 가장 많은 물의 양을 구하기 위해서 투 포인터 사용 + * - 한 높이가 낮으면 다른 높이가 아무리 높아도 의미가 없어서, 작은 높이를 가진 포인터 이동하며 계산 + * - 양 끝에서 포인터 이동할 때마다 최대값 갱신 + * + * 시간복잡도 : O(n) + * - 두 포인터가 배열 양 끝에서 1번씩 이동하므로 + * + * 공간복잡도 : O(1) + * - 포인터(left,right)와 최대값 변수만 사용 + */ +function maxArea(height: number[]): number { + let left = 0, + right = height.length - 1, + maxWater = 0; + + while (left < right) { + const width = right - left; + const minHeight = Math.min(height[left], height[right]); + maxWater = Math.max(maxWater, width * minHeight); + + if (height[left] < height[right]) { + left++; + } else { + right--; + } + } + + return maxWater; +} From 7d0ddc91cbda2a5dae08cb40d55426509d8fb488 Mon Sep 17 00:00:00 2001 From: mmyeon Date: Wed, 15 Jan 2025 14:33:23 +0900 Subject: [PATCH 4/6] add solution : 300. Longest Increasing Subsequence --- longest-increasing-subsequence/mmyeon.ts | 64 ++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 longest-increasing-subsequence/mmyeon.ts diff --git a/longest-increasing-subsequence/mmyeon.ts b/longest-increasing-subsequence/mmyeon.ts new file mode 100644 index 000000000..30674ea74 --- /dev/null +++ b/longest-increasing-subsequence/mmyeon.ts @@ -0,0 +1,64 @@ +/** + * + * @link https://leetcode.com/problems/longest-increasing-subsequence/description/ + * + * 접근 방법 : dp 사용 + * - dp[i]에 i번째 요소 포함한 LIS 길이 저장 + * - 현재 값이 이전값보다 큰 경우, 이전값 순회하면서 LIS 업데이트하기 + * + * 시간복잡도 : O(n^2) + * - 각 nums 순회하면서 이전값 비교하기 위해 순회하니까 O(n^2) + * + * 공간복잡도 : O(n) + * - nums 길이만큼 dp 배열 생성하니까 O(n) + */ + +function lengthOfLIS(nums: number[]): number { + const dp = Array(nums.length).fill(1); + + for (let i = 1; i < nums.length; i++) { + for (let j = 0; j < i; j++) { + if (nums[j] < nums[i]) dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + + return Math.max(...dp); +} + +/** + * + * 접근 방법 : 이진 탐색 사용 + * - num 삽입할 인덱스 찾기 위해서 이진 탐색 사용 + * - nums 배열 순회하면서 각 num의 위치 찾기 + * - 위치가 배열 내에 존재하면 기존 값을 대체하고, 위치가 배열 길이보다 크면 배열에 추가 + * + * 시간복잡도 : O(nlogn) + * - nums 배열의 각 요소에 대해 이진 탐색 O(nlogn) + * + * 공간복잡도 : O(n) + * - arr의 최대 크기가 nums 길이만큼 필요 + */ + +function lengthOfLIS(nums: number[]): number { + const arr: number[] = []; + + nums.forEach((num) => { + let left = 0, + right = arr.length; + // num 삽입할 위치를 이진 탐색으로 찾음 + while (left < right) { + const mid = Math.floor((left + right) / 2); + + if (arr[mid] < num) { + left = mid + 1; + } else right = mid; + } + + // 기존 값 대체 + if (left < arr.length) arr[left] = num; + // 새로운 값 추가 + else arr.push(num); + }); + + return arr.length; +} From 7c5d157c4066bf333e4ee64bc329f2bc78068741 Mon Sep 17 00:00:00 2001 From: mmyeon Date: Thu, 16 Jan 2025 13:57:53 +0900 Subject: [PATCH 5/6] add solution : 54. Spiral Matrix --- spiral-matrix/mmyeon.ts | 59 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 spiral-matrix/mmyeon.ts diff --git a/spiral-matrix/mmyeon.ts b/spiral-matrix/mmyeon.ts new file mode 100644 index 000000000..783df6a97 --- /dev/null +++ b/spiral-matrix/mmyeon.ts @@ -0,0 +1,59 @@ +/** + * @link https://leetcode.com/problems/spiral-matrix/description/ + * + * 접근 방법 : + * - right -> bottom -> left -> top 순서로 진행하면서 경계 업데이트 처리 + * + * 시간복잡도 : O(m x n) + * - m x n 행렬의 모든 숫자 방문하니까 O(m x n) + * + * 공간복잡도 : O(n) + * - 숫자 길이만큼 배열에 담음 * + */ + +function spiralOrder(matrix: number[][]): number[] { + const result: number[] = []; + // 경계 설정 + let top = 0, + bottom = matrix.length - 1, + left = 0, + right = matrix[0].length - 1; + + const moveRight = () => { + for (let i = left; i <= right; i++) { + result.push(matrix[top][i]); + } + top++; + }; + + const moveDown = () => { + for (let i = top; i <= bottom; i++) { + result.push(matrix[i][right]); + } + right--; + }; + + const moveLeft = () => { + for (let i = right; i >= left; i--) { + result.push(matrix[bottom][i]); + } + bottom--; + }; + + const moveUp = () => { + for (let i = bottom; i >= top; i--) { + result.push(matrix[i][left]); + } + left++; + }; + + while (top <= bottom && left <= right) { + moveRight(); + moveDown(); + + if (top <= bottom) moveLeft(); + if (left <= right) moveUp(); + } + + return result; +} From 17ec1480cc71b33637351de91ba761b388b8a4f2 Mon Sep 17 00:00:00 2001 From: mmyeon Date: Fri, 17 Jan 2025 12:47:07 +0900 Subject: [PATCH 6/6] update time and space complexity --- spiral-matrix/mmyeon.ts | 6 +++--- valid-parentheses/mmyeon.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spiral-matrix/mmyeon.ts b/spiral-matrix/mmyeon.ts index 783df6a97..0e934c954 100644 --- a/spiral-matrix/mmyeon.ts +++ b/spiral-matrix/mmyeon.ts @@ -4,11 +4,11 @@ * 접근 방법 : * - right -> bottom -> left -> top 순서로 진행하면서 경계 업데이트 처리 * - * 시간복잡도 : O(m x n) - * - m x n 행렬의 모든 숫자 방문하니까 O(m x n) + * 시간복잡도 : O(n) + * - n은 행렬의 모든 숫자로, 모든 숫자를 한 번씩 방문하므로 O(n) * * 공간복잡도 : O(n) - * - 숫자 길이만큼 배열에 담음 * + * - 모든 숫자를 저장하기 위해서 배열 사용하므로 O(n) */ function spiralOrder(matrix: number[][]): number[] { diff --git a/valid-parentheses/mmyeon.ts b/valid-parentheses/mmyeon.ts index 7d403c208..9ec7da924 100644 --- a/valid-parentheses/mmyeon.ts +++ b/valid-parentheses/mmyeon.ts @@ -9,7 +9,7 @@ * * 공간복잡도 : O(k) * - pairs 객체 고정된 크기로 저장 O(1) - * - stack에 열린 괄호 개수만큼 담기니까 O(k) + * - k는 열린 괄호 개수, stack에 전체 문자열 중 k만큼만 담기니까 O(k) */ function isValid(s: string): boolean {