From e9404536aaffac28ea3a20b2b2475d64e5af2aa1 Mon Sep 17 00:00:00 2001 From: mmyeon <mindfulyeon@gmail.com> Date: Mon, 10 Feb 2025 10:27:33 +0900 Subject: [PATCH 1/5] add solution : 226. Invert Binary Tree --- invert-binary-tree/mmyeon.ts | 63 ++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 invert-binary-tree/mmyeon.ts diff --git a/invert-binary-tree/mmyeon.ts b/invert-binary-tree/mmyeon.ts new file mode 100644 index 000000000..f3152cb9b --- /dev/null +++ b/invert-binary-tree/mmyeon.ts @@ -0,0 +1,63 @@ +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; + } +} + +/** + * @link https://leetcode.com/problems/invert-binary-tree/description/ + * + * 접근 방법 : 깊이 우선 탐색(DFS) 사용 + * - 각 노드의 좌우 자식 노드 swap 진행 + * - 왼쪽, 오른쪽 서브트리에 대해 재귀적으로 호출 + * + * 시간복잡도 : O(n) + * - n은 노드의 개수, 주어진 노드 만큼 순회 + * + * 공간복잡도 : O(h) + * - h : 트리의 높이 + * - 호출 스택 최대 트리 높이만큼 쌓임 + */ +function invertTree(root: TreeNode | null): TreeNode | null { + if (!root) return root; + + [root.left, root.right] = [root.right, root.left]; + invertTree(root.left); + invertTree(root.right); + + return root; +} + +/** + * + * 접근 방법 : 너비 우선 탐색(BFS) 사용 + * - root 노드를 큐에 담고, 큐가 빌 때까지 좌우 자식 노드 swap과 큐에 추가 하는 로직 반복하기 + * + * 시간복잡도 : O(n) + * - n: 트리 노드의 개수 + * - 모든 노드를 한 번 씩 방문하고 swap 작업 진행 + * + * 공간복잡도 : O(n) + * - 최악의 경우 치우친 트리인 경우 모든 노드 순차적으로 큐에 저장 + */ +function invertTree(root: TreeNode | null): TreeNode | null { + if (!root) return root; + + const queue = [root]; + + while (queue.length) { + const node = queue.shift()!; + + [node.left, node.right] = [node.right, node.left]; + + if (node.left) queue.push(node.left); + if (node.right) queue.push(node.right); + } + + return root; +} From 00be0fcedcdf39f94870ec2663fe94c9e2921de4 Mon Sep 17 00:00:00 2001 From: mmyeon <mindfulyeon@gmail.com> Date: Fri, 14 Feb 2025 20:16:50 +0900 Subject: [PATCH 2/5] add solution : 55. Jump Game --- jump-game/mmyeon.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 jump-game/mmyeon.ts diff --git a/jump-game/mmyeon.ts b/jump-game/mmyeon.ts new file mode 100644 index 000000000..e2254bf9b --- /dev/null +++ b/jump-game/mmyeon.ts @@ -0,0 +1,28 @@ +/** + *@link https://leetcode.com/problems/jump-game/description/ + * + * 접근 방법 : + * - 현재 인덱스에서 최대로 도달할 수 있는 인덱스를 갱신하여 마지막 인덱스에 도달할 수 있는지 체크 + * - 최대 도달할 수 있는 인덱스가 현재 인덱스보다 작으면, 이후는 확인할 필요 없으니까 false 리턴 + * + * 시간복잡도 : O(n) + * - n = 배열의 길이, 배열 1회만 순회 + * + * 공간복잡도 : O(1) + * - 고정된 변수만 사용 + */ + +function canJump(nums: number[]): boolean { + const lastIndex = nums.length - 1; + let maxReachableIndex = 0; + + for (let i = 0; i < nums.length; i++) { + if (maxReachableIndex < i) return false; + + maxReachableIndex = Math.max(maxReachableIndex, i + nums[i]); + + if (lastIndex <= maxReachableIndex) return true; + } + + return false; +} From e3a00b42653b2871ee93ba447d7e079d3d0c6c3d Mon Sep 17 00:00:00 2001 From: mmyeon <mindfulyeon@gmail.com> Date: Fri, 14 Feb 2025 23:46:43 +0900 Subject: [PATCH 3/5] add solution : 207. Course Schedule --- course-schedule/mmyeon.ts | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 course-schedule/mmyeon.ts diff --git a/course-schedule/mmyeon.ts b/course-schedule/mmyeon.ts new file mode 100644 index 000000000..0ed72dc19 --- /dev/null +++ b/course-schedule/mmyeon.ts @@ -0,0 +1,62 @@ +/** + * @link https://leetcode.com/problems/course-schedule/description/ + * + * 접근 방법 : + * - 주어진 과목에서 선수 과목 사이클이 존재하는지 확인하기 위해서 dfs 사용 + * - 사이클이 있다는 건 탐색 중에 다시 동일 과목이 등장한 경우니까, 과목별 결과 저장하기 위해서 visited 배열 사용 + * + * 시간복잡도 : O(n + e) + * - n = 들어야 하는 과목 수 + * - e = 선수 과목과 연결된 과목 수 + * - 선수 과목 dfs 호출하고, 선수 과목과 연결된 과목도 호출함 + * + * 공간복잡도 : O(n + e) + * - n = 들어야 하는 과목 수 + * - e = 선수 과목과 연결된 과목 수 + * - visited 배열 : O(n) + * - Map : O(e) + */ + +function canFinish(numCourses: number, prerequisites: number[][]): boolean { + // 선수 과목을 키, 후속 과목을 값으로 저장 + const map = new Map<number, number[]>(); + + for (const [course, prerequisite] of prerequisites) { + if (!map.has(prerequisite)) map.set(prerequisite, []); + map.get(prerequisite)!.push(course); + } + + // 이미 탐색중인 과목인지 확인하기 위한 배열 + // 미탐색 : 0, 탐색중: 1, 탐색완료 : 2로 처리 + const visited = Array(numCourses).fill(0); + + const dfs = (course: number) => { + // 탐색중이면 사이클이 발생 + if (visited[course] === 1) return false; + // 탐색 완료인 과목은 사이클이 없는 상태니까 true 리턴 + if (visited[course] === 2) return true; + + // 탐색 시작 + // 탐색 중인 상태로 변경 + visited[course] = 1; + + const nextCourses = map.get(course) ?? []; + + // 후속 과목 모두 체크 + for (const nextCourse of nextCourses) { + if (!dfs(nextCourse)) return false; + } + + // 탐색 완료 상태로 변경 + visited[course] = 2; + return true; + }; + + // 들어야 하는 모든 과목에 대해 dfs 호출 + for (let i = 0; i < numCourses; i++) { + // 미탐색 노드만 탐색하도록 처리 + if (visited[i] === 0 && !dfs(i)) return false; + } + + return true; +} From 4d28d87d03c9856f78223bf5d9a4f42f91abff8c Mon Sep 17 00:00:00 2001 From: mmyeon <mindfulyeon@gmail.com> Date: Sat, 15 Feb 2025 21:25:35 +0900 Subject: [PATCH 4/5] add solution : 33. Search in Rotated Sorted Array --- search-in-rotated-sorted-array/mmyeon.ts | 53 ++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 search-in-rotated-sorted-array/mmyeon.ts diff --git a/search-in-rotated-sorted-array/mmyeon.ts b/search-in-rotated-sorted-array/mmyeon.ts new file mode 100644 index 000000000..04ed50d71 --- /dev/null +++ b/search-in-rotated-sorted-array/mmyeon.ts @@ -0,0 +1,53 @@ +/** + * @link https://leetcode.com/problems/search-in-rotated-sorted-array/description/ + * + * 접근 방법 : + * - O(log n)으로 풀어야 하니까 이진 탐색으로 탐색 범위 좁히기 + * - pivot 인덱스 찾고, pivot 기준으로 target이 속하는 범위에서 탐색하기 + * + * 시간복잡도 : O(log n) + * - 배열 범위를 계속 줄여나가므로 O(log n) + * + * 공간복잡도 : O(1) + * - 고정된 변수만 사용 + */ + +function search(nums: number[], target: number): number { + let start = 0, + end = nums.length - 1; + + // pivot 인덱스 찾기 + while (start < end) { + const mid = Math.floor((start + end) / 2); + if (nums[mid] > nums[end]) { + start = mid + 1; + } else { + end = mid; + } + } + + const pivot = start; + start = 0; + end = nums.length - 1; + + // pivot 기준으로 target이 포함된 범위로 좁히기 + if (nums[pivot] <= target && target <= nums[end]) { + start = pivot; + } else { + end = pivot - 1; + } + + // target 인덱스 찾기 위해서 이진 탐색 실행 + while (start <= end) { + const mid = Math.floor((start + end) / 2); + + if (nums[mid] === target) return mid; + if (nums[mid] < target) { + start = mid + 1; + } else { + end = mid - 1; + } + } + + return -1; +} From e9d57a682f669f2373cac053d63575a81a6a1761 Mon Sep 17 00:00:00 2001 From: mmyeon <mindfulyeon@gmail.com> Date: Sat, 15 Feb 2025 23:00:24 +0900 Subject: [PATCH 5/5] add solution : 23. Merge k Sorted Lists --- merge-k-sorted-lists/mmyeon.ts | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 merge-k-sorted-lists/mmyeon.ts diff --git a/merge-k-sorted-lists/mmyeon.ts b/merge-k-sorted-lists/mmyeon.ts new file mode 100644 index 000000000..be4b299f9 --- /dev/null +++ b/merge-k-sorted-lists/mmyeon.ts @@ -0,0 +1,61 @@ +class ListNode { + val: number; + next: ListNode | null; + constructor(val?: number, next?: ListNode | null) { + this.val = val === undefined ? 0 : val; + this.next = next === undefined ? null : next; + } +} + +/** + * @link https://leetcode.com/problems/merge-k-sorted-lists/description/ + * + * 접근 방법 : + * - 리스트를 배열에 넣고, 최소값을 가진 노드 찾기 + * - 최소값 노드를 더미 노드에 연결한 뒤 제거하고, 최소값 노드의 다음 노드를 다시 배열에 추가하기 + * - 배열 길이가 0이 될 때까지 반복하기 + * + * 시간복잡도 : O(n * k) + * - n = 총 노드의 개수 + * - k = 리스트의 개수 + * - 최소값 찾고, 최소값 제거하는 로직: O(k) + * - 위의 연산을 총 n번 실행 + * + * 공간복잡도 : O(k) + * - k = 리스트 개수 + * - minList 배열의 크기가 최대 K개까지 유지 + * + */ + +function mergeKLists(lists: Array<ListNode | null>): ListNode | null { + const minList: ListNode[] = []; + + for (const list of lists) { + if (list !== null) minList.push(list); + } + + const dummy = new ListNode(); + let tail = dummy; + + while (minList.length > 0) { + const minIndex = getMinIndex(minList); + const minNode = minList.splice(minIndex, 1)[0]; + + tail.next = minNode; + tail = tail.next; + + if (minNode.next) minList.push(minNode.next); + } + + return dummy.next; +} + +function getMinIndex(nodes: ListNode[]): number { + let minIndex = 0; + + for (let i = 1; i < nodes.length; i++) { + if (nodes[i].val < nodes[minIndex].val) minIndex = i; + } + + return minIndex; +}