diff --git a/binary-tree-maximum-path-sum/soobing.ts b/binary-tree-maximum-path-sum/soobing.ts new file mode 100644 index 000000000..eb2b1287a --- /dev/null +++ b/binary-tree-maximum-path-sum/soobing.ts @@ -0,0 +1,55 @@ +/** + * 문제 설명 + * - 주어진 이진 트리에서 최대 경로 합을 구하는 문제 + * + * 아이디어 + * 1) 분할정복 + DFS + * - DFS 아이디어: 각 노드를 루트로 하는 서브트리에서, 해당 노드를 포함한 최대 경로 합을 구해 전역 변수 maxSum을 업데이트한다. + * - 현재 노드 기준의 최대 경로 합은: 현재 노드의 값 + 왼쪽 서브트리에서의 최대 경로 + 오른쪽 서브트리에서의 최대 경로. + * - 하지만 dfs 함수의 반환값은 부모 노드에 연결될 수 있는 일방향 경로이므로, + * '현재 노드의 값 + (왼쪽 또는 오른쪽 중 더 큰 값)'을 반환해야 한다. + */ + +/** + * 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) + * } + * } + */ + +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 maxPathSum(root: TreeNode | null): number { + let maxSum = -Infinity; + + function dfs(node: TreeNode | null) { + if (!node) return 0; + + const leftMaxSum = Math.max(dfs(node.left), 0); + const rightMaxSum = Math.max(dfs(node.right), 0); + const current = leftMaxSum + rightMaxSum + node.val; + maxSum = Math.max(current, maxSum); + + return node.val + Math.max(leftMaxSum, rightMaxSum); + } + + dfs(root); + + return maxSum; +} diff --git a/graph-valid-tree/soobing.js b/graph-valid-tree/soobing.js new file mode 100644 index 000000000..cdbd0c6b5 --- /dev/null +++ b/graph-valid-tree/soobing.js @@ -0,0 +1,41 @@ +/** + * 문제 설명 + * - 주어진 간선 정보로 트리가 만들어지는지 확인하는 문제 + * - 어려웠음 다시 풀어보기 ⚠️ + * + * 트리의 조건 + * 1) 모든 노드가 연결되어 있어야 한다. + * 2) 사이클이 없어야 한다. + * 3) 총 간선의 갯수는 n-1개여야 한다. + * + * 아이디어 + * 1) Union-Find (Disjoint Set) + * 2) DFS ✅ + */ +function validTree(n, edges) { + if (edges.length !== n - 1) return false; // 간선 수가 n - 1이 아니면 트리 불가 + + const graph = Array.from({ length: n }, () => []); + for (const [a, b] of edges) { + graph[a].push(b); + graph[b].push(a); + } + + const visited = new Set(); + + const dfs = (node, prev) => { + if (visited.has(node)) return false; + visited.add(node); + + for (const neighbor of graph[node]) { + if (neighbor === prev) continue; // 바로 이전 노드는 무시, 체크하는 이유: 무방향 그래프이기 때문에 사이클로 들어가게 하지 않기 위함. + if (!dfs(neighbor, node)) return false; // 사이클 발생 + } + + return true; + }; + + if (!dfs(0, -1)) return false; + + return visited.size === n; +} diff --git a/merge-intervals/soobing.ts b/merge-intervals/soobing.ts new file mode 100644 index 000000000..19b19ee61 --- /dev/null +++ b/merge-intervals/soobing.ts @@ -0,0 +1,28 @@ +/** + * 문제 설명 + * - 주어진 배열의 구간을 병합하는 문제 + * + * 아이디어 + * 1) 그리디 알고리즘 + * - 시작점 기준으로 배열을 정렬 + * - 결과를 담을 merge 배열을 만들고, 첫 번째 구간을 추가한다. 그리고 비교는 두번재 구간부터 순차적으로 진행 + * - 머지된 마지막 구간의 끝보다 현재 구간의 시작점이 작을 경우 머지를 진행하고, 아닌 경우는 머지 배열에 추가한다. + */ +function merge(intervals: number[][]): number[][] { + if (intervals.length === 0) return []; + + intervals.sort((a, b) => a[0] - b[0]); + + const merged: number[][] = [intervals[0]]; + + for (let i = 1; i < intervals.length; i++) { + const lastMerged = merged[merged.length - 1]; + const current = intervals[i]; + if (current[0] <= lastMerged[1]) { + lastMerged[1] = Math.max(current[1], lastMerged[1]); + } else { + merged.push(current); + } + } + return merged; +} diff --git a/missing-number/soobing.ts b/missing-number/soobing.ts new file mode 100644 index 000000000..8d847bf57 --- /dev/null +++ b/missing-number/soobing.ts @@ -0,0 +1,45 @@ +/** + * 문제 설명 + * - 0부터 n까지 숫자가 중복 없이 단 하나만 빠져있는 숫자 찾기 + * + * 아이디어 + * 1) 체크 배열 생성 + * - 0부터 n까지 숫자가 중복 없이 단 하나만 빠져있는 숫자 찾기 + * + * 2) 수학적 접근 + * - n*(n+1)/2: 배열의 합 - 주어진 배열의 합 = 빠진 숫자 + * + * 3) XOR 활용 + * - 0 ^ 1 ^ 2 ^ ... ^ n : 모든 숫자 XOR + * - nums[0] ^ nums[1] ^ ... ^ nums[n-1] : 배열 내 존재하는 숫자들 XOR + * - 이 둘을 XOR하면, 중복되는 숫자는 모두 상쇄되어 빠진 숫자만 남음. + */ + +function missingNumber(nums: number[]): number { + const check = new Array(nums.length).fill(false); + + nums.forEach((num) => (check[num] = true)); + + for (let i = 0; i < nums.length; i++) { + if (!check[i]) return i; + } + return nums.length; +} + +// 수학적 접근 +function missingNumber2(nums: number[]): number { + const n = nums.length; + const expectedSum = (n * (n + 1)) / 2; + const actualSum = nums.reduce((acc, cur) => acc + cur, 0); + return expectedSum - actualSum; +} + +// XOR 활용 +function missingNumber3(nums: number[]): number { + let xor = 0; + for (let i = 0; i < nums.length; i++) { + xor ^= i ^ nums[i]; + } + + return xor ^ nums.length; +} diff --git a/reorder-list/soobing.ts b/reorder-list/soobing.ts new file mode 100644 index 000000000..3028704d3 --- /dev/null +++ b/reorder-list/soobing.ts @@ -0,0 +1,72 @@ +/** + * 문제 설명 + * - 리스트의 중간 지점을 찾아서 뒤쪽 리스트를 뒤집고, 앞쪽 리스트와 뒤쪽 리스트를 병합하는 문제 + * - 어려웠음 다시 풀어보기 ⚠️ + * + * 아이디어 + * 1) 중간 지점 찾기 + * -  투 포인터 사용 + * - 절단할때 사이클 안생기도록 체크 + * 2) 뒤쪽 리스트 뒤집기 + * 3) 병합 + */ +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; + } +} + +/** + Do not return anything, modify head in-place instead. + */ + +function reverseList(head: ListNode | null) { + let prev: ListNode | null = null; + let cur: ListNode | null = head; + + while (cur) { + const next = cur.next; + cur.next = prev; + prev = cur; + cur = next; + } + + return prev; +} + +function reorderList(head: ListNode | null): void { + if (!head || !head.next) return; + + // 1. 중간 지점 찾기 + let slow: ListNode | null = head; + let fast: ListNode | null = head; + + while (fast && fast.next) { + slow = slow!.next; + fast = fast.next.next; + } + + // 2. 리스트 절단 + const secondHead = slow!.next; + slow!.next = null; + + // 3. 뒤쪽 리스트 뒤집기 + let second: ListNode | null = reverseList(secondHead); + + // 4. 병합 + let first: ListNode | null = head; + + while (second) { + let tmp1 = first!.next; + let tmp2 = second.next; + + first!.next = second; + second.next = tmp1; + + first = tmp1; + second = tmp2; + } +}