Skip to content
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

[Jeehay28] WEEK 10 #1010

Merged
merged 6 commits into from
Feb 16, 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
69 changes: 69 additions & 0 deletions course-schedule/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* @param {number} numCourses
* @param {number[][]} prerequisites
* @return {boolean}
*/

// ✅ Graph DFS (Depth-First Search) approach
// Time Complexity: O(V + E), where V is the number of courses (numCourses) and E is the number of prerequisites (edges).
// Space Complexity: O(V + E), where V is the number of courses and E is the number of prerequisites.

var canFinish = function (numCourses, prerequisites) {
Copy link
Contributor

Choose a reason for hiding this comment

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

0, 1, 2 를 상수로 뽑아 내면 어떨까 싶어요. 깔끔한 풀이네요!

// prerequisites = [
// [1, 0], // Course 1 depends on Course 0
// [2, 1], // Course 2 depends on Course 1
// [3, 1], // Course 3 depends on Course 1
// [3, 2] // Course 3 depends on Course 2
// ];

// graph = {
// 0: [1], // Course 0 is a prerequisite for Course 1
// 1: [2, 3], // Course 1 is a prerequisite for Courses 2 and 3
// 2: [3], // Course 2 is a prerequisite for Course 3
// 3: [] // Course 3 has no prerequisites
// };

// Build the graph
const graph = {};

// for(let i=0; i<numCourses; i++) {
// graph[i] = []
// }

// Fill in the graph with prerequisites
for (const [course, prereq] of prerequisites) {
if (!graph[prereq]) {
graph[prereq] = [];
}
graph[prereq].push(course);
}

const visited = new Array(numCourses).fill(0);

const dfs = (course) => {
if (visited[course] === 1) return false; // cycle detected!
if (visited[course] === 2) return true; // already visited

visited[course] = 1;

// // Visit all the courses that depend on the current course
for (const nextCourse of graph[course] || []) {
if (!dfs(nextCourse)) {
return false; // cycle detected!
}
}

visited[course] = 2; // fully visited and return true
return true;
};

// Check for each course whether it's possible to finish it
for (let i = 0; i < numCourses; i++) {
if (!dfs(i)) {
return false; // cycle detected!
}
}

return true; // no cycles
};

91 changes: 91 additions & 0 deletions invert-binary-tree/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {TreeNode}
*/

// ✔️ Recursive Approach
// Time Complexity: O(N), N = Total number of nodes (each node is processed once)
// Space Complexity: O(H), H = Height of the tree (due to recursion stack depth)

var invertTree = function (root) {
if (!root) return null;

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

invertTree(root.left);
invertTree(root.right);

return root;
};

/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {TreeNode}
*/

// ✔️ Stack → DFS approach
// Time Complexity: O(N), N = Total number of nodes (each node is processed once)
// Space Complexity: O(H), H = Height of the tree (due to recursion stack depth)

// var invertTree = function (root) {
// let stack = [root];

// while (stack.length > 0) {
// const node = stack.pop();
// if (!node) continue;

// [node.left, node.right] = [node.right, node.left];
// stack.push(node.left);
// stack.push(node.right);
// }
// return root;
// };


/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root
* @return {TreeNode}
*/

// ✔️ Queue → BFS
// Time Complexity: O(N), N = Total number of nodes (each node is processed once)
// Space Complexity: O(W), W = Maximum width of the tree
// var invertTree = function (root) {
// let queue = [root];

// while (queue.length > 0) {
// const node = queue.shift();
Copy link
Contributor

Choose a reason for hiding this comment

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

shift()의 복잡도는 우선 고려하지 않으신거죠?

실제 코딩 테스트에서도 JS에는 Queue나 Deque이 없기 때문에 우선은 있다고 치고 작성한 뒤 '이 부분은 추가 구현으로 보완 가능하다'고 언급하는 방식으로도 진행한다고 하더라구요.

이전에 달레님의 피드백 중에 비슷한 내용이 있었어서 공유드려요!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아, 그렇군요. 공유 감사합니다. 👍

// if (!node) continue;

// [node.left, node.right] = [node.right, node.left];
// queue.push(node.left);
// queue.push(node.right);
// }
// return root;
// };


101 changes: 101 additions & 0 deletions jump-game/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// 🚀 Greedy approach: much more efficient than the recursive approach
// Time Complexity: O(n)
// Space Complexity: O(1), No extra memory used

/**
* @param {number[]} nums
* @return {boolean}
*/
var canJump = function (nums) {
// ➡️ The farthest position you can reach from any of the previous indices you have already visited.
let farthest = 0;

for (let i = 0; i < nums.length; i++) {
// You cannot reach this position even with your farthest jump value.
if (i > farthest) return false;

// Compare current maximum jump with the previous maximum.
farthest = Math.max(farthest, nums[i] + i);

// Check if you can reach the last index with the current farthest jump.
if (farthest >= nums.length - 1) return true;
}
return false;
};


/**
* @param {number[]} nums
* @return {boolean}
*/

// Time Complexity: O(n)
// Space Complexity: O(n)
var canJump = function (nums) {
let lastIndex = nums.length - 1;

// Initialize memoization array to track visited indices
let memo = new Array(nums.length).fill(undefined);

const dfs = (i) => {
// Base case: if we've reached or surpassed the last index, return true
if (i >= lastIndex) return true;

// If the current index has already been visited, return the stored result
if (memo[i] !== undefined) return memo[i];

// Calculate the farthest position that can be reached from the current index
let maxJump = Math.min(nums[i] + i, lastIndex);

for (let j = i + 1; j <= maxJump; j++) {
if (dfs(j)) {
memo[i] = true;
return true;
}
}

memo[i] = false;
return false;
};

return dfs(0);
};


// 🌀 recursive approach
// ⚠️ Time Complexity: O(2^n) - Exponential due to recursive branching without memoization
// 🔵 Space Complexity: O(n) - Recursive call stack depth

/**
* Check if you can jump to the last index from the first index.
* @param {number[]} nums - Array where nums[i] is the max jump length from position i.
* @return {boolean} True if you can reach the last index, otherwise false.
*/

// var canJump = function (nums) {
// const dfs = (start) => {
// // Base Case: Reached the last index
// if (start === nums.length - 1) {
// return true;
// }

// // Recursive Case: Try all possible jumps
// for (let i = 1; i <= nums[start]; i++) {
// // Example with nums = [2, 3, 1, 1, 4]:
// // start = 1, nums[1] = 3 (can jump 1, 2, or 3 steps)
// // Possible calls:
// // dfs(1 + 1) -> check from index 2
// // dfs(1 + 2) -> check from index 3
// // dfs(1 + 3) -> reached the last index (success)

// if (dfs(start + i)) {
// return true;
// }
// }

// return false; // cannot reach the last index
// };

// return dfs(0);
// };

72 changes: 72 additions & 0 deletions merge-k-sorted-lists/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Brute Force (Array Sorting) - Good for smaller cases
// 🕒 Time Complexity: O(N log N), where N is the total number of nodes
// 🗂️ Space Complexity: O(N)

/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode[]} lists
* @return {ListNode}
*/
var mergeKLists = function (lists) {
Copy link
Contributor

Choose a reason for hiding this comment

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

와우,, 주석이 실시간으로 진화하고 있는것같아요 🫢
지금 풀이도 충분히 좋은 풀이지만, merged-k-sorted-lists라는 이름의 sorted라는 특성을 활용하지 못한 점이 다소 아쉬어요

const nums = [];

// ┌──────────────────────────────────────────────────────────────────┐
// │ Step 1: Extract values from all linked lists │
// │ We traverse each linked list and push node values into 'nums'. │
// │ This flattens the K lists into a single array. │
// └──────────────────────────────────────────────────────────────────┘
for (let list of lists) {
while (list) {
nums.push(list.val);
list = list.next;
}
}

// ┌──────────────────────────────────────────────────────────────────┐
// │ Step 2: Sort the values │
// │ JavaScript's default sort is lexicographical, so we use a custom │
// │ comparator to sort numbers correctly in ascending order. │
// └──────────────────────────────────────────────────────────────────┘
nums.sort((a, b) => a - b);

// ┌──────────────────────────────────────────────────────────────────┐
// │ Step 3: Create a new sorted linked list │
// │ Initialize a dummy node, then iterate through the sorted array. │
// │ For each value, create a new ListNode and append it to the list. │
// └──────────────────────────────────────────────────────────────────┘
let dummy = new ListNode(-1);
let current = dummy;

for (num of nums) {
current.next = new ListNode(num);
current = current.next;
}

// ┌──────────────────────────────────────────────────────────────────┐
// │ Step 4: Return the merged list │
// │ We return dummy.next since dummy is a placeholder. │
// └──────────────────────────────────────────────────────────────────┘
return dummy.next;
};

/**
* ┌──────────────────────────────────────────────────────────────────┐
* │ Time & Space Complexity │
* ├──────────────────────────────────────────────────────────────────┤
* │ Operation │ Complexity │
* ├──────────────────────────────────────────────────────────────────┤
* │ Extracting values │ O(N) - N is the total number of nodes │
* │ Sorting values │ O(N log N) - JavaScript's sort uses Timsort │
* │ Building linked list │ O(N) - Iterates through sorted array │
* ├──────────────────────────────────────────────────────────────────┤
* │ Overall Time Complexity │ O(N log N) │
* ├──────────────────────────────────────────────────────────────────┤
* │ Space Complexity │ O(N) - Storing all node values in array │
* └──────────────────────────────────────────────────────────────────┘
*/
45 changes: 45 additions & 0 deletions search-in-rotated-sorted-array/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/

// ✅ Iterative Binary Search for Rotated Sorted Array
// Time Complexity: O(log N)
// Space Complexity: O(1)
var search = function (nums, target) {
let left = 0;
let right = nums.length - 1;

while (left <= right) {
let pointer = Math.floor((left + right) / 2);

if (nums[pointer] === target) {
return pointer;
}

// Check if the left half is sorted
if (nums[left] <= nums[pointer]) {
// Target is in the sorted left half
if (nums[left] <= target && target < nums[pointer]) {
right = pointer - 1;
} else {
// Target is not in the left half, so search in the right half
left = pointer + 1;
}
} else {
// The right half must be sorted

// Target is in the sorted right half
if (nums[pointer] < target && target <= nums[right]) {
left = pointer + 1;
} else {
// Target is not in the right half, so search in the left half
right = pointer - 1;
}
}
}

return -1;
};