diff --git a/course-schedule/soobing.ts b/course-schedule/soobing.ts new file mode 100644 index 000000000..8d9bb9c28 --- /dev/null +++ b/course-schedule/soobing.ts @@ -0,0 +1,37 @@ +/** + * 문제 해설 + * - 그래프에 사이클이 있는지 없는지 확인하는 문제 + * + * 아이디어 + * 1) 그래프 탐색 -> DFS, BFS + * - 그래프를 탐색하며 사이클이 있는지 없는지 확인한다. + * - 현재 경로에 이미 방문한 노드를 다시 만나면 사이클 발생. = 1 + */ + +function canFinish(numCourses: number, prerequisites: number[][]): boolean { + const graph = new Map(); + const visited: number[] = new Array(numCourses).fill(0); // 0: 안 다녀옴, 1: 사이클 확인 증(방문 중), 2: 사이클 없음 확인 완료 + + // 그래프 생성 + for (const [course, preCourse] of prerequisites) { + if (!graph.has(course)) graph.set(course, []); + graph.get(course)!.push(preCourse); + } + + function hasCycle(index: number) { + if (visited[index] === 1) return true; + if (visited[index] === 2) return false; + + visited[index] = 1; + for (const preCourse of graph.get(index) || []) { + if (hasCycle(preCourse)) return true; + } + visited[index] = 2; + return false; + } + + for (let i = 0; i < numCourses; i++) { + if (hasCycle(i)) return false; + } + return true; +} diff --git a/invert-binary-tree/soobing.ts b/invert-binary-tree/soobing.ts new file mode 100644 index 000000000..f500c4d0c --- /dev/null +++ b/invert-binary-tree/soobing.ts @@ -0,0 +1,64 @@ +/** + * 문제 설명 + * - 이진 트리를 반전시키는 문제 + * + * 아이디어 + * 1) DFS / BFS 로 탐색하면서 반전시키기 + * - 시간 복잡도 O(n): 모든 노드 한번씩 방문 + * - 공간 복잡도 DFS의 경우 O(h), BFS의 경우 O(2/n) -> 마지막 레벨의 노드 수 + */ + +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; + } +} + +function invertTree(root: TreeNode | null): TreeNode | null { + if (!root) return null; + + const left = invertTree(root.left); + const right = invertTree(root.right); + + root.left = right; + root.right = left; + + return root; +} + +/** + * Definition for a binary tree node. + * 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) + * } + * } + */ + +function invertTreeBFS(root: TreeNode | null): TreeNode | null { + const queue: (TreeNode | null)[] = [root]; + + while (queue.length > 0) { + const current = queue.shift(); + if (current) { + const left = current.left; + const right = current.right; + current.left = right; + current.right = left; + + queue.push(left); + queue.push(right); + } + } + return root; +} diff --git a/jump-game/soobing.ts b/jump-game/soobing.ts new file mode 100644 index 000000000..75067dd70 --- /dev/null +++ b/jump-game/soobing.ts @@ -0,0 +1,17 @@ +/** + * 문제 해설 + * - 현재 위치에서 최대 점프할 수 있는 갯수를 담고 있는 배열, 마지막 항목에 도달이 가능한지 반환하는 문제 + * + * 아이디어 + * 1) 그리디 알고리즘 + * - 배열을 쭉 순회하면서 다음 이동 가능한 횟수를 비교하여 다음 항목으로 이동이 가능한지 체크, 없다면 false 반환. + * - 일단 현재까지 왔다면 이후에 최대로 갈 수 있는 값을 업데이트. + */ +function canJump(nums: number[]): boolean { + let reachable = 0; + for (let i = 0; i < nums.length; i++) { + if (i > reachable) return false; + reachable = Math.max(reachable, i + nums[i]); + } + return true; +} diff --git a/merge-k-sorted-lists/soobing.ts b/merge-k-sorted-lists/soobing.ts new file mode 100644 index 000000000..04756c411 --- /dev/null +++ b/merge-k-sorted-lists/soobing.ts @@ -0,0 +1,56 @@ +/** + * 문제 설명 + * - k개의 정렬된 링크드 리스트 병합하기 + * + * 아이디어 + * 1) 병합정렬 -> 대표적인 분할 정복(Divide and Conquer) 문제 + * - 두 리스트를 병합할 수 있는 함수를 계속해서 적용한다. + * - 두 링크드 리스트 병합하는 예제 + * ㄴ @link https://leetcode.com/problems/merge-two-sorted-lists/description/ + * + */ +/** + * Definition for singly-linked list. + * 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) + * } + * } + */ + +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; + } +} + +function mergeList(list1: ListNode | null, list2: ListNode | null) { + const dummy = new ListNode(0); + let current = dummy; + + while (list1 && list2) { + if (list1.val <= list2.val) { + current.next = list1; + list1 = list1.next; + } else { + current.next = list2; + list2 = list2.next; + } + current = current.next; + } + current.next = list1 || list2; + + return dummy.next; +} + +function mergeKLists(lists: Array): ListNode | null { + return lists.reduce((acc, current) => { + return mergeList(acc, current); + }, null); +} diff --git a/search-in-rotated-sorted-array/soobing.ts b/search-in-rotated-sorted-array/soobing.ts new file mode 100644 index 000000000..23d64481c --- /dev/null +++ b/search-in-rotated-sorted-array/soobing.ts @@ -0,0 +1,38 @@ +/** + * 문제 설명 + * - 회전된 정렬된 배열에서 타겟 값을 찾는 문제 + * - 이진 탐색의 응용 버전 + * + * 아이디어 + * 1) 변형된 이진 탐색 사용 + * - 중간 값과 왼쪽, 끝 값을 비교하여 왼쪽 정렬 영역인지 오른쪽 정렬 영역인지 확인 + */ + +function search(nums: number[], target: number): number { + let left = 0; + let right = nums.length - 1; + + while (left <= right) { + const mid = Math.floor((left + right) / 2); + + if (nums[mid] === target) return mid; + + // mid가 왼쪽 정렬에 포함 + if (nums[mid] >= nums[left]) { + if (target >= nums[left] && target < nums[mid]) { + right = mid - 1; + } else { + left = mid + 1; + } + } + // mid가 오른쪽 정렬에 포함 + else { + if (target < nums[mid] && target <= nums[right]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + } + return -1; +}