-
-
Notifications
You must be signed in to change notification settings - Fork 361
[Zero-1016] WEEK 02 solutions #2694
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /** | ||
| * 시간 복잡도 O(n²) | ||
| * 공간 복잡도 O(log n) | ||
| * | ||
| * 접근: 오름차순 정렬 + 투 포인터 | ||
| * - 기준 인덱스 i를 고정하고, left = i + 1, right = 끝에서 탐색 | ||
| * - 정렬된 상태에서 같은 값을 건너뛰는 방식으로 중복 제거 (Set 불필요) | ||
| */ | ||
| function threeSum(nums: number[]): number[][] { | ||
| const result: number[][] = []; | ||
| nums.sort((a, b) => a - b); // 오름차순 정렬 | ||
|
|
||
| for (let i = 0; i < nums.length - 2; i++) { | ||
| // 기준 숫자가 이전과 같으면 동일한 조합이 나오므로 건너뜀 | ||
| if (i > 0 && nums[i] === nums[i - 1]) continue; | ||
|
|
||
| // 최적화: 기준 숫자가 양수면 뒤의 숫자들도 모두 양수 → 합이 0 불가능 | ||
| if (nums[i] > 0) break; | ||
|
|
||
| let left = i + 1; | ||
| let right = nums.length - 1; | ||
|
|
||
| while (left < right) { | ||
| const sum = nums[i] + nums[left] + nums[right]; | ||
|
|
||
| if (sum === 0) { | ||
| result.push([nums[i], nums[left], nums[right]]); | ||
|
|
||
| while (left < right && nums[left] === nums[left + 1]) left++; | ||
| while (left < right && nums[right] === nums[right - 1]) right--; | ||
|
|
||
| left++; | ||
| right--; | ||
| } else if (sum < 0) { | ||
| left++; | ||
| } else { | ||
| right--; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return result; | ||
| } |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| /** | ||
| * 시간 복잡도 O(n) | ||
| * 공간 복잡도 O(n) | ||
| */ | ||
| function climbStairs(n: number): number { | ||
| // NOTE: n이 1일 경우 바로 종료 | ||
| if (n === 1) return 1; | ||
|
|
||
| const dp = new Array(n).fill(0); | ||
|
|
||
| dp[0] = 1; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dp[i]가 0부터 시작하다보니, 마치 0개 계단의 경우 1칸 이동을 나타내는 것처럼 느껴집니다.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 의견 감사합니다! 사실 저도 dp[1] 부터 사용하는걸 생각하긴 했는데요. dp[0]을 비워두면 실제로 쓰이지 않는 값이 배열에 남는 게 어색해서, 슬롯을 전부 의미 있게 채우는 쪽(dp[i] = i+1칸의 답)으로 작성했어요. 공간 차이는 미미하지만, "배열의 모든 원소가 유효한 답"이라는 일관성을 유지하고 싶었습니다. |
||
| dp[1] = 2; | ||
|
|
||
| for (let i = 2; i < dp.length; i++) { | ||
| dp[i] = dp[i - 1] + dp[i - 2]; | ||
| } | ||
|
|
||
| return dp[dp.length - 1]; | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| /** | ||
| * 시간복잡도 O(n) | ||
| * 공간복잡도 O(n) | ||
| */ | ||
| function productExceptSelf(nums: number[]): number[] { | ||
| const n_length = nums.length; | ||
| const result = new Array(n_length).fill(1); | ||
|
|
||
| // NOTE: 왼쪽 -> 오른쪽, 오른쪽 -> 왼쪽 순회하면서 값을 누적함. | ||
| let left = 1; | ||
| for (let i = 0; i < n_length; i++) { | ||
| result[i] *= left; | ||
| left *= nums[i]; | ||
| } | ||
|
|
||
| let right = 1; | ||
| for (let i = n_length - 1; i >= 0; i--) { | ||
| result[i] *= right; | ||
| right *= nums[i]; | ||
| } | ||
|
|
||
| return result; | ||
| } |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| /** | ||
| * 242. Valid Anagram | ||
| * Given two strings s and t, return true if t is an anagram of s, and false otherwise. | ||
| * | ||
| * 시간복잡도: O(n) | ||
| * 공간복잡도: O(n) | ||
| */ | ||
| function isAnagram(s: string, t: string): boolean { | ||
| if (s.length !== t.length) return false; | ||
|
|
||
| const sMap = new Map<string, number>(); | ||
| const tMap = new Map<string, number>(); | ||
|
|
||
| // 문자열 s와 t를 순회하면서 각 문자의 빈도수를 계산 | ||
| for (let i = 0; i < s.length; i++) { | ||
| sMap.set(s[i], (sMap.get(s[i]) || 0) + 1); | ||
| tMap.set(t[i], (tMap.get(t[i]) || 0) + 1); | ||
| } | ||
|
|
||
| // 문자열 s와 t의 빈도수를 비교 | ||
| for (const [key, value] of sMap) { | ||
| if (value !== tMap.get(key)) return false; | ||
| } | ||
|
|
||
| return true; | ||
| } |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| class TreeNode { | ||
| val: number; | ||
| left: TreeNode | null; | ||
| right: TreeNode | null; | ||
| constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { | ||
| this.val = val === undefined ? 0 : val; | ||
| this.left = left === undefined ? null : left; | ||
| this.right = right === undefined ? null : right; | ||
| } | ||
| } | ||
| type Frame = { | ||
| node: TreeNode | null; | ||
| min: number; | ||
| max: number; | ||
| }; | ||
|
|
||
| /** | ||
| * 반복적 DFS로 각 노드가 부모 노드가 정한 값 범위 (min, max) 안에 있는지 검사한다. | ||
| * - 왼쪽 자식으로 내려가면 상한(max)이 현재 노드 값으로 좁아지고, | ||
| * - 오른쪽 자식으로 내려가면 하한(min)이 현재 노드 값으로 좁아진다. | ||
| * | ||
| * 시간 복잡도: O(n) — 모든 노드를 한 번씩 방문 | ||
| * 공간 복잡도: O(n) — 최악의 경우 스택 크기 (평균적으로는 O(h)) | ||
| */ | ||
| function isValidBST(root: TreeNode | null): boolean { | ||
| const stack: Frame[] = [{ node: root, min: -Infinity, max: Infinity }]; | ||
|
|
||
| while (stack.length > 0) { | ||
| const { node, min, max } = stack.pop()!; | ||
| if (!node) continue; | ||
|
|
||
| // NOTE 부모 노드들이 정한 범위를 벗어나면 유효한 BST가 아니다. | ||
| if (node.val <= min || node.val >= max) return false; | ||
|
|
||
| stack.push({ node: node.left, min, max: node.val }); | ||
| stack.push({ node: node.right, min: node.val, max }); | ||
| } | ||
|
|
||
| return true; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
풀이 1:
threeSum— Time: ✅ O(n²) → O(n^2) / Space: ❌ O(log n) → O(1)피드백: 정렬과 투 포인터를 이용해 모든 3합을 찾고, 중복을 양 끝에서 건너뛰는 방식으로 제거합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 2:
climbStairs— Time: ❌ O(n²) → O(n) / Space: ❌ O(log n) → O(n)피드백: 앞선 두 수의 합으로 현재의 경우의 수를 구하는 일반적인 DP 풀이입니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 3:
productExceptSelf— Time: ❌ O(n²) → O(n) / Space: ❌ O(log n) → O(1)피드백: 두 방향 누적곱으로 모든 원소의 곱을 구하는 표준 풀이입니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 4:
isAnagram— Time: ❌ O(n²) → O(n) / Space: ❌ O(log n) → O(1)피드백: 두 맵을 사용해 빈도수를 비교하되, 배열 기반 카운트가 더 빠르고 간결합니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 5:
isValidBST— Time: O(n) / Space: O(n)피드백: 각 노드에 대해 유효한 범위를 추적하는 표준 방식입니다.
개선 제안: 현재 구현이 적절해 보입니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
제가 작성한 복잡도랑 다르게 표기가 되어있네요 :(