Skip to content

[mallayon] Week 10 #1011

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
Feb 15, 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
62 changes: 62 additions & 0 deletions course-schedule/mmyeon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* @link https://leetcode.com/problems/course-schedule/description/
*
* 접근 방법 :
* - 주어진 과목에서 선수 과목 사이클이 존재하는지 확인하기 위해서 dfs 사용
* - 사이클이 있다는 건 탐색 중에 다시 동일 과목이 등장한 경우니까, 과목별 결과 저장하기 위해서 visited 배열 사용
*
* 시간복잡도 : O(n + e)
* - n = 들어야 하는 과목 수
* - e = 선수 과목과 연결된 과목 수
* - 선수 과목 dfs 호출하고, 선수 과목과 연결된 과목도 호출함
*
* 공간복잡도 : O(n + e)
* - n = 들어야 하는 과목 수
* - e = 선수 과목과 연결된 과목 수
* - visited 배열 : O(n)
* - Map : O(e)
*/

function canFinish(numCourses: number, prerequisites: number[][]): boolean {
// 선수 과목을 키, 후속 과목을 값으로 저장
const map = new Map<number, number[]>();

for (const [course, prerequisite] of prerequisites) {
if (!map.has(prerequisite)) map.set(prerequisite, []);
map.get(prerequisite)!.push(course);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-null assertion operator 라는 것을 사용할 수도 있군요. 새로운 걸 배우고 갑니다. 감사합니다.

}

// 이미 탐색중인 과목인지 확인하기 위한 배열
// 미탐색 : 0, 탐색중: 1, 탐색완료 : 2로 처리
const visited = Array(numCourses).fill(0);

const dfs = (course: number) => {
// 탐색중이면 사이클이 발생
if (visited[course] === 1) return false;
// 탐색 완료인 과목은 사이클이 없는 상태니까 true 리턴
if (visited[course] === 2) return true;

// 탐색 시작
// 탐색 중인 상태로 변경
visited[course] = 1;

const nextCourses = map.get(course) ?? [];

// 후속 과목 모두 체크
for (const nextCourse of nextCourses) {
if (!dfs(nextCourse)) return false;
}

// 탐색 완료 상태로 변경
visited[course] = 2;
return true;
};

// 들어야 하는 모든 과목에 대해 dfs 호출
for (let i = 0; i < numCourses; i++) {
// 미탐색 노드만 탐색하도록 처리
if (visited[i] === 0 && !dfs(i)) return false;
}

return true;
}
63 changes: 63 additions & 0 deletions invert-binary-tree/mmyeon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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;
}
}

/**
* @link https://leetcode.com/problems/invert-binary-tree/description/
*
* 접근 방법 : 깊이 우선 탐색(DFS) 사용
* - 각 노드의 좌우 자식 노드 swap 진행
* - 왼쪽, 오른쪽 서브트리에 대해 재귀적으로 호출
*
* 시간복잡도 : O(n)
* - n은 노드의 개수, 주어진 노드 만큼 순회
*
* 공간복잡도 : O(h)
* - h : 트리의 높이
* - 호출 스택 최대 트리 높이만큼 쌓임
*/
function invertTree(root: TreeNode | null): TreeNode | null {
if (!root) return root;

[root.left, root.right] = [root.right, root.left];
invertTree(root.left);
invertTree(root.right);

return root;
}

/**
*
* 접근 방법 : 너비 우선 탐색(BFS) 사용
* - root 노드를 큐에 담고, 큐가 빌 때까지 좌우 자식 노드 swap과 큐에 추가 하는 로직 반복하기
*
* 시간복잡도 : O(n)
* - n: 트리 노드의 개수
* - 모든 노드를 한 번 씩 방문하고 swap 작업 진행
*
* 공간복잡도 : O(n)
* - 최악의 경우 치우친 트리인 경우 모든 노드 순차적으로 큐에 저장
*/
function invertTree(root: TreeNode | null): TreeNode | null {
if (!root) return root;

const queue = [root];

while (queue.length) {
const node = queue.shift()!;

[node.left, node.right] = [node.right, node.left];

if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}

return root;
}
28 changes: 28 additions & 0 deletions jump-game/mmyeon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
*@link https://leetcode.com/problems/jump-game/description/
*
* 접근 방법 :
* - 현재 인덱스에서 최대로 도달할 수 있는 인덱스를 갱신하여 마지막 인덱스에 도달할 수 있는지 체크
* - 최대 도달할 수 있는 인덱스가 현재 인덱스보다 작으면, 이후는 확인할 필요 없으니까 false 리턴
*
* 시간복잡도 : O(n)
* - n = 배열의 길이, 배열 1회만 순회
*
* 공간복잡도 : O(1)
* - 고정된 변수만 사용
*/

function canJump(nums: number[]): boolean {
const lastIndex = nums.length - 1;
let maxReachableIndex = 0;

for (let i = 0; i < nums.length; i++) {
if (maxReachableIndex < i) return false;

maxReachableIndex = Math.max(maxReachableIndex, i + nums[i]);

if (lastIndex <= maxReachableIndex) return true;
}

return false;
}
61 changes: 61 additions & 0 deletions merge-k-sorted-lists/mmyeon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
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;
}
}

/**
* @link https://leetcode.com/problems/merge-k-sorted-lists/description/
*
* 접근 방법 :
* - 리스트를 배열에 넣고, 최소값을 가진 노드 찾기
* - 최소값 노드를 더미 노드에 연결한 뒤 제거하고, 최소값 노드의 다음 노드를 다시 배열에 추가하기
* - 배열 길이가 0이 될 때까지 반복하기
*
* 시간복잡도 : O(n * k)
* - n = 총 노드의 개수
* - k = 리스트의 개수
* - 최소값 찾고, 최소값 제거하는 로직: O(k)
* - 위의 연산을 총 n번 실행
*
* 공간복잡도 : O(k)
* - k = 리스트 개수
* - minList 배열의 크기가 최대 K개까지 유지
*
*/

function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
const minList: ListNode[] = [];

for (const list of lists) {
if (list !== null) minList.push(list);
}

const dummy = new ListNode();
let tail = dummy;

while (minList.length > 0) {
const minIndex = getMinIndex(minList);
const minNode = minList.splice(minIndex, 1)[0];

tail.next = minNode;
tail = tail.next;

if (minNode.next) minList.push(minNode.next);
}

return dummy.next;
}

function getMinIndex(nodes: ListNode[]): number {
let minIndex = 0;

for (let i = 1; i < nodes.length; i++) {
if (nodes[i].val < nodes[minIndex].val) minIndex = i;
}

return minIndex;
}
53 changes: 53 additions & 0 deletions search-in-rotated-sorted-array/mmyeon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* @link https://leetcode.com/problems/search-in-rotated-sorted-array/description/
*
* 접근 방법 :
* - O(log n)으로 풀어야 하니까 이진 탐색으로 탐색 범위 좁히기
* - pivot 인덱스 찾고, pivot 기준으로 target이 속하는 범위에서 탐색하기
*
* 시간복잡도 : O(log n)
* - 배열 범위를 계속 줄여나가므로 O(log n)
*
* 공간복잡도 : O(1)
* - 고정된 변수만 사용
*/

function search(nums: number[], target: number): number {
let start = 0,
end = nums.length - 1;

// pivot 인덱스 찾기
while (start < end) {
const mid = Math.floor((start + end) / 2);
if (nums[mid] > nums[end]) {
start = mid + 1;
} else {
end = mid;
}
}

const pivot = start;
start = 0;
end = nums.length - 1;

// pivot 기준으로 target이 포함된 범위로 좁히기
if (nums[pivot] <= target && target <= nums[end]) {
start = pivot;
} else {
end = pivot - 1;
}

// target 인덱스 찾기 위해서 이진 탐색 실행
while (start <= end) {
const mid = Math.floor((start + end) / 2);

if (nums[mid] === target) return mid;
if (nums[mid] < target) {
start = mid + 1;
} else {
end = mid - 1;
}
}

return -1;
}