diff --git a/best-time-to-buy-and-sell-stock/mmyeon.ts b/best-time-to-buy-and-sell-stock/mmyeon.ts new file mode 100644 index 000000000..156d94a71 --- /dev/null +++ b/best-time-to-buy-and-sell-stock/mmyeon.ts @@ -0,0 +1,24 @@ +/** + * + * 접근 방법 : + * - max profit을 구하는 문제로 O(n)으로 풀기 + * - 현재 가격에서 가장 낮은 가격을 뺀 값을 max profit으로 설정 + * + * 시간복잡도 : O(n) + * - n은 prices 길이, 요소 1회 순회하니까 O(n) + * + * 공간복잡도 : O(1) + * - 변수 2개 사용하니까 O(1) + */ + +function maxProfit(prices: number[]): number { + let minPrice = prices[0], + maxProfit = 0; + + for (const price of prices) { + minPrice = Math.min(price, minPrice); + maxProfit = Math.max(maxProfit, price - minPrice); + } + + return maxProfit; +} diff --git a/encode-and-decode-strings/mmyeon.ts b/encode-and-decode-strings/mmyeon.ts new file mode 100644 index 000000000..f8b744b9f --- /dev/null +++ b/encode-and-decode-strings/mmyeon.ts @@ -0,0 +1,67 @@ +/* + * 시간복잡도 : O(n) + * - 배열 1회 순회하면서 문자열 합치기 + * + * 공간복잡도 : O(1) + */ +function encode(strs: string[]): string { + let result = strs[0]; + + for (let i = 1; i < strs.length; i++) { + result += "#" + strs[i]; + } + return result; +} +/* + * 시간복잡도 : O(n) + * - 문자 순회하면서 # 기준으로 나눔 + * + * 공간복잡도 : O(n) + * - 문자열 길이만큼 생성해서 리턴 + */ +function decode(encoded: string): string[] { + return encoded.split("#"); +} + +// 스택 활용하는 방법 +/* + * 시간복잡도 : O(n) + * + * 공간복잡도 : O(1) + */ + +// ["Hello","World"] => 5#Hello5#World +function encode(strs: string[]): string { + let result = ""; + for (const str of strs) { + result += `${str.length}#${str}`; + } + return result; +} + +/* + * 접근 방법 : + * - 배열 길이를 포함해서 encode한 뒤 decode할 때 길이 활용헤서 stack에 담는 방식 + * + * 시간복잡도 : O(n) + * - 인코딩된 문자열 1회 순회 + * + * 공간복잡도 : O(n) + * - n은 result 길이 + */ + +// 5#Hello5#World => ["Hello","World"] +function decode(encoded: string): string[] { + const result: string[] = []; + let index = 0; + while (index < encoded.length) { + const separatorIndex = encoded.indexOf("#", index); + const length = parseInt(encoded.slice(index, separatorIndex), 10); + index = separatorIndex + 1; + const str = encoded.slice(index, index + length); + result.push(str); + index += length; + } + + return result; +} diff --git a/group-anagrams/mmyeon.ts b/group-anagrams/mmyeon.ts new file mode 100644 index 000000000..98da77aab --- /dev/null +++ b/group-anagrams/mmyeon.ts @@ -0,0 +1,31 @@ +/** + * + * 접근 방법 : + * - group anagrams 저장할 map 선언하기 + * - 단어 문자열 strs 순회하면서 단어 정렬하기 + * - map에 존재하지 않는 경우, 배열 형태로 맵에 저장하기. + * - map에 존재하는 경우, 기존 값에 현재 단어 추가하기 + * - map의 값들만 리턴하기 + * + * 시간복잡도 : O(n * klogk) + * - n은 strs 길이, k는 단어 길이 + * - strs 배열 n번 순회 : O(n) + * - 각 단어 정렬 : O(klogk) + * + * 공간복잡도 : O(n) + * - map에 strs 길이만큼 저장하니까 + */ +function groupAnagrams(strs: string[]): string[][] { + const map = new Map(); + + for (const str of strs) { + const sortedWord = str.split("").sort().join(""); + if (map.get(sortedWord)) { + map.get(sortedWord)!.push(str); + } else { + map.set(sortedWord, [str]); + } + } + + return [...map.values()]; +} diff --git a/implement-trie-prefix-tree/mmyeon.ts b/implement-trie-prefix-tree/mmyeon.ts new file mode 100644 index 000000000..5ddaac76f --- /dev/null +++ b/implement-trie-prefix-tree/mmyeon.ts @@ -0,0 +1,76 @@ +class TrieNode { + children: Map; + isEndOfWord: boolean; + + constructor() { + // 키 : 문자, 값 : 다음 문자 노드 + this.children = new Map(); + this.isEndOfWord = false; + } +} + +/** + * @link https://leetcode.com/problems/implement-trie-prefix-tree/description/ + * + * 접근 방법 : + * - root 노드에서 시작해서 각 문자열을 children노드에 저장한다. + * - 문자열의 마지막 문자일 때 끝을 나타내기 위해서 isEndOfWord값을 true로 변경해준다. + * - insert : 각 문자열 순회하면서, 문자에 대한 children노드가 없으면 생성해서 문자와 다음 노드를 연결해준다. + * + * 시간복잡도 : O(n) + * - 문자 길이 n 만큼 문자 삽입함 + * + * 공간복잡도 : O(n) + * - 최대 문자열 길이만큼 노드 추가됨 + */ + +class Trie { + root: TrieNode; + + constructor() { + this.root = new TrieNode(); + } + + insert(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) as TrieNode; + } + currentNode.isEndOfWord = true; + } + + search(word: string): boolean { + let currentNode = this.root; + + for (const letter of word) { + if (!currentNode.children.has(letter)) return false; + currentNode = currentNode.children.get(letter) as TrieNode; + } + return currentNode.isEndOfWord; + } + + startsWith(prefix: string): boolean { + let currentNode = this.root; + + for (const letter of prefix) { + if (!currentNode.children.has(letter)) return false; + currentNode = currentNode.children.get(letter) as TrieNode; + } + return true; + } +} + +/** + * Your Trie object will be instantiated and called as such: + * var obj = new Trie() + * obj.insert(word) + * var param_2 = obj.search(word) + * var param_3 = obj.startsWith(prefix) + */ diff --git a/word-break/mmyeon.ts b/word-break/mmyeon.ts new file mode 100644 index 000000000..741d13add --- /dev/null +++ b/word-break/mmyeon.ts @@ -0,0 +1,32 @@ +// "leetcode" +// [ true, false, false, false, true, false, false, false, true ] +/** + * 접근 방법 : DP 바텀업 방식 + * - 문자열 늘려가면서, 부분 문자열에 wordDict에 포함되는지 체크 + * + * 시간복잡도 : O(n^2) + * - n은 문자열 s의 길이 + * - 외부 반복문에서 문자열 순회(n번), 내부 반복문에서 최대 n번 순회 + * + * 공간복잡도 : O(n) + * - 문자열 길이 n만큼 dp 배열 저장 + */ +function wordBreak(s: string, wordDict: string[]): boolean { + const set = new Set(wordDict); + const dp = Array(s.length + 1).fill(false); + // 빈 문자열은 항상 나눌 수 있으니까 true + dp[0] = true; + + // i는 문자열의 끝 인덱스 + for (let i = 1; i <= s.length; i++) { + // j는 문자열의 시작 인덱스 + for (let j = 0; j < i; j++) { + if (dp[j] && set.has(s.substring(j, i))) { + dp[i] = true; + break; + } + } + } + + return dp[s.length]; +}