From 900d30102516f414336a50a50623540eb5a32106 Mon Sep 17 00:00:00 2001 From: yoouyeon Date: Thu, 3 Apr 2025 16:14:59 +0900 Subject: [PATCH 1/5] add: Two Sum solution --- two-sum/yoouyeon.ts | 84 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 two-sum/yoouyeon.ts diff --git a/two-sum/yoouyeon.ts b/two-sum/yoouyeon.ts new file mode 100644 index 000000000..6c5b2b1c6 --- /dev/null +++ b/two-sum/yoouyeon.ts @@ -0,0 +1,84 @@ +/** + * Solution 1. 배열에서 짝 찾기 + * + * [Idea] + * 단순하게 nums를 순회하면서 현재 원소(num)과 합해서 target을 만들 수 있는 원소가 있는지 확인함 + * + * [Time Complexity] + * O(n^2) (n: nums의 원소 개수) + * nums를 순회하면서 (O(n)) Array.indexOf 메소드로 짝이 되는 원소를 찾는 방식 (O(n)) + * O(n)이 중첩되어 있으므로 O(n^2) + * + * [Space Complexity] + * O(1) + */ +function twoSum1(nums: number[], target: number): number[] { + for (let idx = 0; idx < nums.length - 1; idx++) { + const complementIdx = nums.indexOf(target - nums[idx], idx + 1); + if (complementIdx !== -1) { + return [idx, complementIdx]; + } + } + // 타입 에러를 제거하기 위해 추가한 코드. + // 답이 무조건 존재한다는 조건이 있었으므로 이 코드에는 도달하지 않는다. + return []; +} + +/** + * Solution 2. Map을 이용하기 - 1 + * + * [Idea] + * O(n^2)보다 작게 만들 수 있는 방법은 인덱스를 찾는 데 걸리는 시간을 줄이는 것이라 생각해서 검색 시간이 짧은 Map을 활용함. + * + * [Time Complexity] + * O(n) (n: nums의 원소 개수) + * Map을 생성할 때 O(n) + * Map에서 짝이 되는 원소를 찾는 것은 O(1) + * + * [Space Complexity] + * O(n) (n: nums의 원소 개수) + * Map을 생성할 때 필요한 공간 n + */ +function twoSum2(nums: number[], target: number): number[] { + const numMap = nums.reduce((map, num, idx) => { + map.set(num, idx); + return map; + }, new Map()); + + for (let idx = 0; idx < nums.length; idx++) { + const complementIdx = numMap.get(target - nums[idx]); + if (complementIdx !== undefined && complementIdx !== idx) { + return [idx, complementIdx]; + } + } + + return []; +} + +/** + * Solution 3. Map을 이용하기 - 2 + * + * [Idea] + * Map을 미리 생성하지 않고 짝을 찾는 for loop에서 Map을 생성한다. + * [a, b]가 답일 때 지금까지는 a를 기준으로 b를 찾았지만 지금은 b를 기준으로 a를 찾게 되는 것! + * + * [Time Complexity] + * O(n) (n: nums의 원소 개수) + * + * [Space Complexity] + * O(n) (n: nums의 원소 개수) + * Solution 1보다 조금 메모리를 덜 쓴다. + */ +function twoSum3(nums: number[], target: number): number[] { + const numMap = new Map(); + + for (let idx = 0; idx < nums.length; idx++) { + const complementIdx = numMap.get(target - nums[idx]); + if (complementIdx !== undefined) { + return [idx, complementIdx]; + } + numMap.set(nums[idx], idx); + } + + return []; +} From 6f8cc1a6c8b8ab7a9f6d7bc5fd54b1b6c7597143 Mon Sep 17 00:00:00 2001 From: yoouyeon Date: Thu, 3 Apr 2025 16:17:17 +0900 Subject: [PATCH 2/5] add: Contains Duplicate solution --- contains-duplicate/yoouyeon.ts | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 contains-duplicate/yoouyeon.ts diff --git a/contains-duplicate/yoouyeon.ts b/contains-duplicate/yoouyeon.ts new file mode 100644 index 000000000..345e8bb26 --- /dev/null +++ b/contains-duplicate/yoouyeon.ts @@ -0,0 +1,46 @@ +/** + * Solution 1. Set을 활용하기 1 + * + * [Idea] + * nums에서 중복된 원소를 제거하기 위해서 nums를 이용해서 중복을 허용하지 않는 자료구조인 Set을 생성 (numSet) + * 중복된 원소가 있었다면 set을 생성할 때 제거되어 nums의 길이와 numSet의 크기가 다를 것이므로 비교해서 결과를 반환 + * + * [Time Complexity] + * O(n) (n: nums의 원소 개수) + * nums 배열을 이용해서 Set을 만드는 것에서 O(n) + * + * [Space Complexity] + * O(n) (n: nums의 원소 개수) + * numSet을 생성하기 위해 최대 n만큼의 공간이 추가로 필요함 + */ +function containsDuplicate1(nums: number[]): boolean { + const numsSet = new Set(nums); + return numsSet.size !== nums.length; +} + +/** + * Solution 2. Set을 활용하기 2 + * + * [Idea] + * 최악의 경우가 아닐 때 약간 더 빠르게 결과를 반환할 수 있는 방법 + * 중복인 원소를 마주하자마자 바로 반환하기 때문에 아주 조금 더 빠름 + * + * [Time Complexity] + * O(n) (n: nums의 원소 개수) + * + * [Space Complexity] + * O(n) (n: nums의 원소 개수) + * 생성자로 추가하는 것과 add로 추가하는 차이 때문인지 실제로 사용된 메모리는 Solution 1보다 조금 많다. + */ +function containsDuplicate2(nums: number[]): boolean { + const numSet = new Set(); + + for (const num of nums) { + if (numSet.has(num)) { + return true; + } + numSet.add(num); + } + + return false; +} From ba7602e963a29ed3fe79b932d14967bbbcf4b2a9 Mon Sep 17 00:00:00 2001 From: yoouyeon Date: Thu, 3 Apr 2025 17:25:42 +0900 Subject: [PATCH 3/5] add: Top K Frequent Elements solution --- top-k-frequent-elements/yoouyeon.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 top-k-frequent-elements/yoouyeon.ts diff --git a/top-k-frequent-elements/yoouyeon.ts b/top-k-frequent-elements/yoouyeon.ts new file mode 100644 index 000000000..db770cba9 --- /dev/null +++ b/top-k-frequent-elements/yoouyeon.ts @@ -0,0 +1,29 @@ +/** + * [Idea] + * 숫자와 등장 횟수를 세는 counter Map을 활용했다. + * 1. counter Map을 만든 뒤 + * 2. 배열로 변환 + * 3. count 내림차순으로 정렬 + * 4. 정답 배열 만들기 + * + * [Time Complexity] + * O(n + m log m) => O(n log n) (n: nums의 길이, m: nums에서 unique elements의 개수) + * - counter Map을 생성할 때 O(n) + * - counter를 배열로 변환해서 정렬할 때 O(m log m) + * - sortedCounter를 k 길이로 자르고 count만 담은 배열로 만들 때 O(k) + * + * [Space Complexity] + * O(m + k) => O(n) + * - counter Map의 O(m) + * - 정답 배열을 만들 때 추가로 사용하는 O(k) + */ +function topKFrequent1(nums: number[], k: number): number[] { + const counter = new Map(); // key: 숫자, value: count + for (const num of nums) { + const count = counter.get(num); + counter.set(num, count === undefined ? 1 : count + 1); + } + + const sortedCounter = [...counter].sort((a, b) => b[1] - a[1]); + return sortedCounter.slice(0, k).map((count) => count[0]); +} From b1436f32b9c23ac22a896901406cf068c7acdaa8 Mon Sep 17 00:00:00 2001 From: yoouyeon Date: Fri, 4 Apr 2025 21:20:18 +0900 Subject: [PATCH 4/5] add: Longest Consecutive Sequence solution --- longest-consecutive-sequence/yoouyeon.ts | 38 ++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 longest-consecutive-sequence/yoouyeon.ts diff --git a/longest-consecutive-sequence/yoouyeon.ts b/longest-consecutive-sequence/yoouyeon.ts new file mode 100644 index 000000000..0b3990512 --- /dev/null +++ b/longest-consecutive-sequence/yoouyeon.ts @@ -0,0 +1,38 @@ +/** + * [Idea] + * O(n)이기 때문에 정렬 방식은 불가능하다고 판단함. => 특별한 방법이 생각이 안나서 일일이 구간을 확인해 주는 방식을 시도했다. + * 배열을 순회할 때 빠르게 원소를 찾아야 하기 때문에 Set을 이용하기로 함. + * + * [Time Complexity] + * O(n + n) => O(n) + * - Set 생성: O(n) + * - for loop: O(n) + * for loop 내부에 while loop가 있긴 하지만 "증가하는 구간의 시작점일 때만 실행되기 때문에" (이걸 놓쳐서 시간 초과 났었다..) + * 각 원소에 접근하는 횟수는 결국 1번뿐. + * + * [Space Complexity] + * O(n) + * Set을 생성하기 위해 추가로 필요한 공간 + */ + +function longestConsecutive(nums: number[]): number { + const numSet = new Set(nums); + let longest = 0; + + for (const startNum of numSet) { + // 증가하는 구간의 시작점인 경우에만 검사한다. (같은 구간을 중복해서 탐색하는 것을 막기) + // nums.length가 10000인 경우에 뜬 Time Limit Exceeded를 해결하기 위해 추가함... + if (numSet.has(startNum - 1)) { + continue; + } + let length = 1; + let currNum = startNum + 1; + while (numSet.has(currNum)) { + length++; + currNum++; + } + longest = Math.max(longest, length); + } + + return longest; +} From 287b0056caf761e318dd707d430ae794a29701b2 Mon Sep 17 00:00:00 2001 From: yoouyeon Date: Fri, 4 Apr 2025 22:10:16 +0900 Subject: [PATCH 5/5] add: House Robber solution --- house-robber/yoouyeon.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 house-robber/yoouyeon.ts diff --git a/house-robber/yoouyeon.ts b/house-robber/yoouyeon.ts new file mode 100644 index 000000000..63afb9877 --- /dev/null +++ b/house-robber/yoouyeon.ts @@ -0,0 +1,32 @@ +/** + * [Idea] + * 어떤 집을 턴다고 했을 때 최대 금액을 구하는 식은 아래와 같이 세울 수 있다.: + * Math.max(전 집을 털었을 때의 최대 금액, 전전 집을 털었을 때의 최대 금액 + 지금 집을 털었을 때 얻는 금액) => DP + * 연산 횟수를 줄여주기 위해서 메모 배열을 이용했다. + * + * [Time Complexity] + * O(n) + * for loop 에서 nums 배열의 각 원소에 한번씩만 접근하므로 O(n) + * + * [Space Complexity] + * O(n) + * 메모 배열을 위한 추가 공간 + */ +function rob(nums: number[]): number { + const n = nums.length; + if (n === 1) { + return nums[0]; + } + + // idx 집을 터는 경우 vs 안 터는 경우를 비교해서 큰 값을 저장하는 dp 배열 + const memo = new Array(n).fill(0); + memo[0] = nums[0]; + memo[1] = Math.max(memo[0], nums[1]); + + for (let idx = 2; idx < n; idx++) { + // idx번째 집에서의 최대 금액 = idx번째 집을 터는 경우 vs 안 터는 경우 중 최댓값 + memo[idx] = Math.max(memo[idx - 2] + nums[idx], memo[idx - 1]); + } + + return memo[n - 1]; +}