Skip to content

[soobing] WEEK11 Solutions #1562

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions binary-tree-maximum-path-sum/soobing.ts
Original file line number Diff line number Diff line change
@@ -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;
}
41 changes: 41 additions & 0 deletions graph-valid-tree/soobing.js
Original file line number Diff line number Diff line change
@@ -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;
}
28 changes: 28 additions & 0 deletions merge-intervals/soobing.ts
Original file line number Diff line number Diff line change
@@ -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;
}
45 changes: 45 additions & 0 deletions missing-number/soobing.ts
Original file line number Diff line number Diff line change
@@ -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;
}
72 changes: 72 additions & 0 deletions reorder-list/soobing.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}