Skip to content

Commit 2d21160

Browse files
authored
Merge pull request #1562 from soobing/week11
[soobing] WEEK11 Solutions
2 parents bb13635 + 1fb9052 commit 2d21160

File tree

5 files changed

+241
-0
lines changed

5 files changed

+241
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* 문제 설명
3+
* - 주어진 이진 트리에서 최대 경로 합을 구하는 문제
4+
*
5+
* 아이디어
6+
* 1) 분할정복 + DFS
7+
* - DFS 아이디어: 각 노드를 루트로 하는 서브트리에서, 해당 노드를 포함한 최대 경로 합을 구해 전역 변수 maxSum을 업데이트한다.
8+
* - 현재 노드 기준의 최대 경로 합은: 현재 노드의 값 + 왼쪽 서브트리에서의 최대 경로 + 오른쪽 서브트리에서의 최대 경로.
9+
* - 하지만 dfs 함수의 반환값은 부모 노드에 연결될 수 있는 일방향 경로이므로,
10+
* '현재 노드의 값 + (왼쪽 또는 오른쪽 중 더 큰 값)'을 반환해야 한다.
11+
*/
12+
13+
/**
14+
* Definition for a binary tree node.
15+
* class TreeNode {
16+
* val: number
17+
* left: TreeNode | null
18+
* right: TreeNode | null
19+
* constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
20+
* this.val = (val===undefined ? 0 : val)
21+
* this.left = (left===undefined ? null : left)
22+
* this.right = (right===undefined ? null : right)
23+
* }
24+
* }
25+
*/
26+
27+
class TreeNode {
28+
val: number;
29+
left: TreeNode | null;
30+
right: TreeNode | null;
31+
constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
32+
this.val = val === undefined ? 0 : val;
33+
this.left = left === undefined ? null : left;
34+
this.right = right === undefined ? null : right;
35+
}
36+
}
37+
38+
function maxPathSum(root: TreeNode | null): number {
39+
let maxSum = -Infinity;
40+
41+
function dfs(node: TreeNode | null) {
42+
if (!node) return 0;
43+
44+
const leftMaxSum = Math.max(dfs(node.left), 0);
45+
const rightMaxSum = Math.max(dfs(node.right), 0);
46+
const current = leftMaxSum + rightMaxSum + node.val;
47+
maxSum = Math.max(current, maxSum);
48+
49+
return node.val + Math.max(leftMaxSum, rightMaxSum);
50+
}
51+
52+
dfs(root);
53+
54+
return maxSum;
55+
}

graph-valid-tree/soobing.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/**
2+
* 문제 설명
3+
* - 주어진 간선 정보로 트리가 만들어지는지 확인하는 문제
4+
* - 어려웠음 다시 풀어보기 ⚠️
5+
*
6+
* 트리의 조건
7+
* 1) 모든 노드가 연결되어 있어야 한다.
8+
* 2) 사이클이 없어야 한다.
9+
* 3) 총 간선의 갯수는 n-1개여야 한다.
10+
*
11+
* 아이디어
12+
* 1) Union-Find (Disjoint Set)
13+
* 2) DFS ✅
14+
*/
15+
function validTree(n, edges) {
16+
if (edges.length !== n - 1) return false; // 간선 수가 n - 1이 아니면 트리 불가
17+
18+
const graph = Array.from({ length: n }, () => []);
19+
for (const [a, b] of edges) {
20+
graph[a].push(b);
21+
graph[b].push(a);
22+
}
23+
24+
const visited = new Set();
25+
26+
const dfs = (node, prev) => {
27+
if (visited.has(node)) return false;
28+
visited.add(node);
29+
30+
for (const neighbor of graph[node]) {
31+
if (neighbor === prev) continue; // 바로 이전 노드는 무시, 체크하는 이유: 무방향 그래프이기 때문에 사이클로 들어가게 하지 않기 위함.
32+
if (!dfs(neighbor, node)) return false; // 사이클 발생
33+
}
34+
35+
return true;
36+
};
37+
38+
if (!dfs(0, -1)) return false;
39+
40+
return visited.size === n;
41+
}

merge-intervals/soobing.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* 문제 설명
3+
* - 주어진 배열의 구간을 병합하는 문제
4+
*
5+
* 아이디어
6+
* 1) 그리디 알고리즘
7+
* - 시작점 기준으로 배열을 정렬
8+
* - 결과를 담을 merge 배열을 만들고, 첫 번째 구간을 추가한다. 그리고 비교는 두번재 구간부터 순차적으로 진행
9+
* - 머지된 마지막 구간의 끝보다 현재 구간의 시작점이 작을 경우 머지를 진행하고, 아닌 경우는 머지 배열에 추가한다.
10+
*/
11+
function merge(intervals: number[][]): number[][] {
12+
if (intervals.length === 0) return [];
13+
14+
intervals.sort((a, b) => a[0] - b[0]);
15+
16+
const merged: number[][] = [intervals[0]];
17+
18+
for (let i = 1; i < intervals.length; i++) {
19+
const lastMerged = merged[merged.length - 1];
20+
const current = intervals[i];
21+
if (current[0] <= lastMerged[1]) {
22+
lastMerged[1] = Math.max(current[1], lastMerged[1]);
23+
} else {
24+
merged.push(current);
25+
}
26+
}
27+
return merged;
28+
}

missing-number/soobing.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* 문제 설명
3+
* - 0부터 n까지 숫자가 중복 없이 단 하나만 빠져있는 숫자 찾기
4+
*
5+
* 아이디어
6+
* 1) 체크 배열 생성
7+
* - 0부터 n까지 숫자가 중복 없이 단 하나만 빠져있는 숫자 찾기
8+
*
9+
* 2) 수학적 접근
10+
* - n*(n+1)/2: 배열의 합 - 주어진 배열의 합 = 빠진 숫자
11+
*
12+
* 3) XOR 활용
13+
* - 0 ^ 1 ^ 2 ^ ... ^ n : 모든 숫자 XOR
14+
* - nums[0] ^ nums[1] ^ ... ^ nums[n-1] : 배열 내 존재하는 숫자들 XOR
15+
* - 이 둘을 XOR하면, 중복되는 숫자는 모두 상쇄되어 빠진 숫자만 남음.
16+
*/
17+
18+
function missingNumber(nums: number[]): number {
19+
const check = new Array(nums.length).fill(false);
20+
21+
nums.forEach((num) => (check[num] = true));
22+
23+
for (let i = 0; i < nums.length; i++) {
24+
if (!check[i]) return i;
25+
}
26+
return nums.length;
27+
}
28+
29+
// 수학적 접근
30+
function missingNumber2(nums: number[]): number {
31+
const n = nums.length;
32+
const expectedSum = (n * (n + 1)) / 2;
33+
const actualSum = nums.reduce((acc, cur) => acc + cur, 0);
34+
return expectedSum - actualSum;
35+
}
36+
37+
// XOR 활용
38+
function missingNumber3(nums: number[]): number {
39+
let xor = 0;
40+
for (let i = 0; i < nums.length; i++) {
41+
xor ^= i ^ nums[i];
42+
}
43+
44+
return xor ^ nums.length;
45+
}

reorder-list/soobing.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/**
2+
* 문제 설명
3+
* - 리스트의 중간 지점을 찾아서 뒤쪽 리스트를 뒤집고, 앞쪽 리스트와 뒤쪽 리스트를 병합하는 문제
4+
* - 어려웠음 다시 풀어보기 ⚠️
5+
*
6+
* 아이디어
7+
* 1) 중간 지점 찾기
8+
* -  투 포인터 사용
9+
* - 절단할때 사이클 안생기도록 체크
10+
* 2) 뒤쪽 리스트 뒤집기
11+
* 3) 병합
12+
*/
13+
class ListNode {
14+
val: number;
15+
next: ListNode | null;
16+
constructor(val?: number, next?: ListNode | null) {
17+
this.val = val === undefined ? 0 : val;
18+
this.next = next === undefined ? null : next;
19+
}
20+
}
21+
22+
/**
23+
Do not return anything, modify head in-place instead.
24+
*/
25+
26+
function reverseList(head: ListNode | null) {
27+
let prev: ListNode | null = null;
28+
let cur: ListNode | null = head;
29+
30+
while (cur) {
31+
const next = cur.next;
32+
cur.next = prev;
33+
prev = cur;
34+
cur = next;
35+
}
36+
37+
return prev;
38+
}
39+
40+
function reorderList(head: ListNode | null): void {
41+
if (!head || !head.next) return;
42+
43+
// 1. 중간 지점 찾기
44+
let slow: ListNode | null = head;
45+
let fast: ListNode | null = head;
46+
47+
while (fast && fast.next) {
48+
slow = slow!.next;
49+
fast = fast.next.next;
50+
}
51+
52+
// 2. 리스트 절단
53+
const secondHead = slow!.next;
54+
slow!.next = null;
55+
56+
// 3. 뒤쪽 리스트 뒤집기
57+
let second: ListNode | null = reverseList(secondHead);
58+
59+
// 4. 병합
60+
let first: ListNode | null = head;
61+
62+
while (second) {
63+
let tmp1 = first!.next;
64+
let tmp2 = second.next;
65+
66+
first!.next = second;
67+
second.next = tmp1;
68+
69+
first = tmp1;
70+
second = tmp2;
71+
}
72+
}

0 commit comments

Comments
 (0)