diff --git a/non-overlapping-intervals/soobing.ts b/non-overlapping-intervals/soobing.ts new file mode 100644 index 000000000..ab84597b0 --- /dev/null +++ b/non-overlapping-intervals/soobing.ts @@ -0,0 +1,25 @@ +/** + * 문제 설명 + * - 겹치지 않는 최대한 많은 구간을 선택하는 문제 (그러기 위해서는 몇개를 제거해야하는지) + * + * 아이디어 + * 1) 그리디 알고리즘 + * - 최대한 많은 구간 선택을 위해서는 끝나는 시간을 기준으로 정렬 + * - 순회 하면서 다음 시작 시간이 현재 끝나는 시간보다 크거나 같으면 카운트 증가 + */ +function eraseOverlapIntervals(intervals: number[][]): number { + if (intervals.length === 0) return 0; + + intervals.sort((a, b) => a[1] - b[1]); + + let count = 1; + let end = intervals[0][1]; + + for (let i = 1; i < intervals.length; i++) { + if (intervals[i][0] >= end) { + count++; + end = intervals[i][1]; + } + } + return intervals.length - count; +} diff --git a/number-of-connected-components-in-an-undirected-graph/soobing.ts b/number-of-connected-components-in-an-undirected-graph/soobing.ts new file mode 100644 index 000000000..9f803201a --- /dev/null +++ b/number-of-connected-components-in-an-undirected-graph/soobing.ts @@ -0,0 +1,74 @@ +/** + * 문제 설명 + * - 무방향 그래프에서 연결된 노드의 갯수를 구하는 문제 + * + * 아이디어 + * 1) 그래프 생성 + DFS로 탐색 + * 2) Union-Find -> ⚠️ 다음에 이걸로 해보기 + * - 모든 노드를 자기 자신을 부모로 초기화 + * - 각 edge에 대해 union 연산 수행 + * - 최종적으로 남아있는 루트(대표 노드)의 개수가 연결 요소 수 + */ +function countComponents(n: number, edges: number[][]): number { + const graph: Record = {}; + for (let i = 0; i < n; i++) graph[i] = []; + + for (const [a, b] of edges) { + graph[a].push(b); + graph[b].push(a); + } + + const visited = new Set(); + let count = 0; + + const dfs = (node: number) => { + visited.add(node); + for (const neighbor of graph[node]) { + if (!visited.has(neighbor)) { + dfs(neighbor); + } + } + }; + + for (let i = 0; i < n; i++) { + if (!visited.has(i)) { + dfs(i); + count++; + } + } + + return count; +} + +function countComponents2(n: number, edges: number[][]): number { + const parent = Array(n) + .fill(0) + .map((_, i) => i); + + // find 함수 (경로 압축 포함) + const find = (x: number): number => { + if (parent[x] !== x) { + parent[x] = find(parent[x]); + } + return parent[x]; + }; + + // union 함수 (다른 집합이면 병합하고 true 반환) + const union = (x: number, y: number): boolean => { + const rootX = find(x); + const rootY = find(y); + if (rootX === rootY) return false; + parent[rootX] = rootY; + return true; + }; + + let count = n; + + for (const [a, b] of edges) { + if (union(a, b)) { + count--; // 서로 다른 집합을 연결했으므로 연결 요소 수 줄임 + } + } + + return count; +} diff --git a/remove-nth-node-from-end-of-list/soobing.ts b/remove-nth-node-from-end-of-list/soobing.ts new file mode 100644 index 000000000..a99db223c --- /dev/null +++ b/remove-nth-node-from-end-of-list/soobing.ts @@ -0,0 +1,48 @@ +/** + * 문제 설명 + * - 연결 리스트의 "끝"에서 n번째 노드를 제거하는 문제 + * + * 아이디어 + * 1) 투 포인터 기법 ⚠️ + * - fast, slow 포인터간의 간격을 n + 1만큼 벌리면 fast 포인터가 끝에 도달했을 경우, slow 포인터를 활용하여 제거 가능하다. + * - 주의할점은 노드가 하나만 존재하는 케이스를 대응하기 위해 더미 노드를 사용한다. + * + */ +/** + * 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 removeNthFromEnd(head: ListNode | null, n: number): ListNode | null { + let dummy = new ListNode(0, head); + let fast: ListNode | null = dummy; + let slow: ListNode | null = dummy; + + for (let i = 0; i < n + 1; i++) { + fast = fast!.next; + } + + while (fast) { + fast = fast.next; + slow = slow!.next; + } + + slow!.next = slow!.next!.next; + return dummy.next; +} diff --git a/same-tree/soobing.ts b/same-tree/soobing.ts new file mode 100644 index 000000000..eae3fe463 --- /dev/null +++ b/same-tree/soobing.ts @@ -0,0 +1,48 @@ +/** + * 문제 설명 + * - 두 이진 트리가 같은지 확인하는 문제 + * + * 아이디어 + * 1) 트리 탐색 (BFS, DFS)을 하면서 비교한다. + */ + +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 isSameTree(p: TreeNode | null, q: TreeNode | null): boolean { + if (!p && !q) return true; + if (!p || !q) return false; + if (p.val !== q.val) return false; + + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); +} + +function isSameTreeBFS(p: TreeNode | null, q: TreeNode | null): boolean { + const queue1: (TreeNode | null)[] = [p]; + const queue2: (TreeNode | null)[] = [q]; + + while (queue1.length > 0 && queue2.length > 0) { + const node1 = queue1.shift(); + const node2 = queue2.shift(); + + if (!node1 && !node2) continue; + if (!node1 || !node2) return false; + if (node1.val !== node2.val) return false; + + queue1.push(node1.left); + queue1.push(node1.right); + queue2.push(node2.left); + queue2.push(node2.right); + } + + return queue1.length === queue2.length; +} diff --git a/serialize-and-deserialize-binary-tree/soobing.ts b/serialize-and-deserialize-binary-tree/soobing.ts new file mode 100644 index 000000000..60fbe564e --- /dev/null +++ b/serialize-and-deserialize-binary-tree/soobing.ts @@ -0,0 +1,125 @@ +/** + * 문제 설명 + * - 이진 트리를 직렬화/역직렬화 하는 문제 + * + * 아이디어 + * 1) 트리를 순회하면서 value값만 담는다. + * - DFS, BFS 둘 다 가능 + * - DFS는 재귀 기반이라 코드가 간결하고, BFS 보다 메모리 사용량은 적지만, 깊은 트리에서는 스택 오버플로우가 발생할 수 있다. + * - 반면에 BFS는 큐를 많이 사용해서 폭이 넓은 트리에서 비효율적일 수 있지만, + */ + +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; + } +} + +/* + * Encodes a tree to a single string. + */ +function serialize(root: TreeNode | null): string { + const result: string[] = []; + + const dfs = (node: TreeNode | null) => { + if (node === null) { + result.push("null"); + return; + } + + result.push(node.val.toString()); + dfs(node.left); + dfs(node.right); + }; + + dfs(root); + return result.join(","); +} + +/* + * Decodes your encoded data to tree. + */ +function deserialize(data: string): TreeNode | null { + const values = data.split(","); + let i = 0; + const dfs = () => { + if (values[i] === "null") { + i++; + return null; + } + + const node = new TreeNode(Number(values[i])); + i++; + node.left = dfs(); + node.right = dfs(); + return node; + }; + return dfs(); +} + +/** + * Your functions will be called as such: + * deserialize(serialize(root)); + */ + +/* + * Encodes a tree to a single string. + */ +function serializeBFS(root: TreeNode | null): string { + if (!root) return "null"; + + const queue: (TreeNode | null)[] = [root]; + const result: string[] = []; + + while (queue.length > 0) { + const node = queue.shift(); + if (node) { + result.push(node.val.toString()); + queue.push(node.left); + queue.push(node.right); + } else { + result.push("null"); + } + } + return result.join(","); +} + +/* + * Decodes your encoded data to tree. + */ +function deserializeBFS(data: string): TreeNode | null { + const values = data.split(","); + + if (values[0] === "null") return null; + + const root = new TreeNode(Number(values[0])); + let i = 1; + const queue: (TreeNode | null)[] = [root]; + + while (queue.length > 0 && i < values.length) { + const current = queue.shift(); + const left = values[i++]; + const right = values[i++]; + + if (left !== "null") { + current!.left = new TreeNode(Number(left)); + queue.push(current!.left); + } + + if (right !== "null") { + current!.right = new TreeNode(Number(right)); + queue.push(current!.right); + } + } + return root; +} + +/** + * Your functions will be called as such: + * deserialize(serialize(root)); + */