Skip to content

[soobing] WEEK12 Solutions #1587

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 5 commits into from
Jun 22, 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
25 changes: 25 additions & 0 deletions non-overlapping-intervals/soobing.ts
Original file line number Diff line number Diff line change
@@ -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;
}
74 changes: 74 additions & 0 deletions number-of-connected-components-in-an-undirected-graph/soobing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* 문제 설명
* - 무방향 그래프에서 연결된 노드의 갯수를 구하는 문제
*
* 아이디어
* 1) 그래프 생성 + DFS로 탐색
* 2) Union-Find -> ⚠️ 다음에 이걸로 해보기
* - 모든 노드를 자기 자신을 부모로 초기화
* - 각 edge에 대해 union 연산 수행
* - 최종적으로 남아있는 루트(대표 노드)의 개수가 연결 요소 수
*/
function countComponents(n: number, edges: number[][]): number {
const graph: Record<number, number[]> = {};
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<number>();
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;
}
48 changes: 48 additions & 0 deletions remove-nth-node-from-end-of-list/soobing.ts
Original file line number Diff line number Diff line change
@@ -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;
}
48 changes: 48 additions & 0 deletions same-tree/soobing.ts
Original file line number Diff line number Diff line change
@@ -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;
}
125 changes: 125 additions & 0 deletions serialize-and-deserialize-binary-tree/soobing.ts
Original file line number Diff line number Diff line change
@@ -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));
*/