diff --git a/graph-valid-tree/mike2ox.ts b/graph-valid-tree/mike2ox.ts new file mode 100644 index 000000000..1183fe43c --- /dev/null +++ b/graph-valid-tree/mike2ox.ts @@ -0,0 +1,45 @@ +/** + * Source: https://www.lintcode.com/problem/178/ + * Solution: 유효한 트리인지 순회하면서 확인하면 되기에 BFS로 구현 + * 시간 복잡도: O(V + E) - 노드와 간선에 한번은 방문 + * 공간 복잡도: O(V + E) - 인접리스트 만큼의 공간 필요 + */ +function validTree(n: number, edges: number[][]): boolean { + // 간선 개수 체크: 트리는 노드 개수 - 1개의 간선을 가져야 함 + if (edges.length !== n - 1) return false; + + // 인접 리스트 + const adjList: Map = new Map(); + for (let i = 0; i < n; i++) { + adjList.set(i, []); + } + + // 양방향 그래프 구성 + for (const [u, v] of edges) { + adjList.get(u)!.push(v); + adjList.get(v)!.push(u); + } + + const queue: [number, number][] = [[0, -1]]; // [노드, 부모노드] + const visited: Set = new Set([0]); + + while (queue.length > 0) { + const [node, parent] = queue.shift()!; + + // 모든 이웃 노드 확인 + for (const neighbor of adjList.get(node)!) { + // 부모 노드는 continue + if (neighbor === parent) continue; + + // 이미 방문한 노드를 만나면 사이클 존재 + if (visited.has(neighbor)) return false; + + // 이웃 노드를 큐에 추가하고 방문 표시 + visited.add(neighbor); + queue.push([neighbor, node]); + } + } + + // 모든 노드가 연결되어 있는지 확인 + return visited.size === n; +} diff --git a/maximum-depth-of-binary-tree/mike2ox.ts b/maximum-depth-of-binary-tree/mike2ox.ts new file mode 100644 index 000000000..c7410b177 --- /dev/null +++ b/maximum-depth-of-binary-tree/mike2ox.ts @@ -0,0 +1,47 @@ +/** + * Source: https://leetcode.com/problems/maximum-depth-of-binary-tree/ + * 접근법: 최대 깊이만 고려하면 되기에 탐색 알고리즘 중 하나 선택 + * + * 시간복잡도: O(N) - 편향 트리인 경우, 모든 노드(N개) 검색 + * 공간복잡도: O(H) - 트리높이 + * + * 다른 접근법 + * - 재귀를 통해 가독성있는 코드 작성 가능(But, 깊이가 커지면 스택오버플로우가 + */ + +/** + * 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) + * } + * } + */ + +// solution1: array를 stack처럼 사용하면서 DFS구현 +function maxDepth(root: TreeNode | null): number { + if (!root) return 0; + const stack = new Array({ node: root, depth: 1 }); + let maxDepth = 1; + while (stack.length) { + const now = stack.pop(); + if (!now.node?.left && !now.node?.right) { + if (now.depth > maxDepth) maxDepth = now.depth; + continue; + } + stack.push({ node: now.node.left, depth: now.depth + 1 }); + stack.push({ node: now.node.right, depth: now.depth + 1 }); + } + return maxDepth; +} + +// solution2: recursion으로 DFS구현 +function maxDepth(root: TreeNode | null): number { + if (!root) return 0; + return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +} diff --git a/merge-intervals/mike2ox.ts b/merge-intervals/mike2ox.ts new file mode 100644 index 000000000..1d2fb4b02 --- /dev/null +++ b/merge-intervals/mike2ox.ts @@ -0,0 +1,27 @@ +/** + * Source: https://leetcode.com/problems/merge-intervals/ + * 풀이방법: 정령후 첫번째 구간 추가하고 이후 순회하면서 겹치는지 확인 후 병합 or 추가 + * 시간복잡도: O(NlogN) - 정렬에서 NlogN + * 공간복잡도: O(N) - 결과 저장할 공간 + */ + +function merge(intervals: number[][]): number[][] { + if (intervals.length <= 1) return intervals; + + intervals.sort((a, b) => a[0] - b[0]); + const result: number[][] = [intervals[0]]; + + for (let i = 1; i < intervals.length; i++) { + const current = intervals[i]; + const lastMerged = result[result.length - 1]; + + // 현재 구간의 시작점이 이전 구간의 끝점보다 작거나 같으면 merge + if (current[0] <= lastMerged[1]) { + lastMerged[1] = Math.max(lastMerged[1], current[1]); // 끝점을 두 구간의 끝점 중 더 큰 값으로 업데이트 + } else { + result.push(current); + } + } + + return result; +} diff --git a/reorder-list/mike2ox.ts b/reorder-list/mike2ox.ts new file mode 100644 index 000000000..6fb2b1e43 --- /dev/null +++ b/reorder-list/mike2ox.ts @@ -0,0 +1,55 @@ +/** + * Source: https://leetcode.com/problems/reorder-list/ + * 풀이방법: 임시 배열을 사용해서 투포인트 전략으로 풂 + * 시간복잡도: O(n) + * 공간복잡도: O(n) + * + * 추가 풀이 + * - node를 가리키는 두 인자만 사용해서 투포인트 전략이 가능(but, 구현 x) +/ + +/** + * 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) + * } + * } + */ + +/** + Do not return anything, modify head in-place instead. + */ +function reorderList(head: ListNode | null): void { + if (!head || !head.next) return; + + // 1. 모든 노드를 배열에 저장 + const nodes: ListNode[] = []; + let current: ListNode | null = head; + while (current) { + nodes.push(current); + current = current.next; + } + + // 2. 배열의 양끝에서 시작하여 리스트 재구성 + let left = 0; + let right = nodes.length - 1; + + while (left < right) { + // 현재 왼쪽 노드의 다음을 저장 + nodes[left].next = nodes[right]; + left++; + + if (left === right) break; + + // 현재 오른쪽 노드를 다음 왼쪽 노드에 연결 + nodes[right].next = nodes[left]; + right--; + } + + // 마지막 노드의 next를 null로 설정 + nodes[left].next = null; +}