From 1665715d13b8b5c369d800da38e379f3a2d6fa14 Mon Sep 17 00:00:00 2001 From: jeehay Date: Sun, 9 Feb 2025 20:25:53 +0900 Subject: [PATCH 1/6] Add invert-binary-tree solution --- invert-binary-tree/Jeehay28.js | 91 ++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 invert-binary-tree/Jeehay28.js diff --git a/invert-binary-tree/Jeehay28.js b/invert-binary-tree/Jeehay28.js new file mode 100644 index 000000000..ab798240e --- /dev/null +++ b/invert-binary-tree/Jeehay28.js @@ -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(); +// if (!node) continue; + +// [node.left, node.right] = [node.right, node.left]; +// queue.push(node.left); +// queue.push(node.right); +// } +// return root; +// }; + + From 9b3c728fa4c4185ae0904293bc1fbd3c0d43b56a Mon Sep 17 00:00:00 2001 From: jeehay Date: Sun, 9 Feb 2025 20:59:05 +0900 Subject: [PATCH 2/6] Add search-in-rotated-sorted-array solution --- search-in-rotated-sorted-array/Jeehay28.js | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 search-in-rotated-sorted-array/Jeehay28.js diff --git a/search-in-rotated-sorted-array/Jeehay28.js b/search-in-rotated-sorted-array/Jeehay28.js new file mode 100644 index 000000000..376586a77 --- /dev/null +++ b/search-in-rotated-sorted-array/Jeehay28.js @@ -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; +}; + From be15f0de45ebbd9fa847179460eb1c827167fe82 Mon Sep 17 00:00:00 2001 From: jeehay Date: Sun, 9 Feb 2025 22:55:57 +0900 Subject: [PATCH 3/6] Add course-schedule solution --- course-schedule/Jeehay28.js | 69 +++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 course-schedule/Jeehay28.js diff --git a/course-schedule/Jeehay28.js b/course-schedule/Jeehay28.js new file mode 100644 index 000000000..98523d297 --- /dev/null +++ b/course-schedule/Jeehay28.js @@ -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) { + // 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 { + 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 +}; + From e7e150686e69b093ed6bc5ccedddc049f5039d08 Mon Sep 17 00:00:00 2001 From: jeehay Date: Fri, 14 Feb 2025 00:19:47 +0900 Subject: [PATCH 4/6] Add jump-game solution --- jump-game/Jeehay28.js | 101 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 jump-game/Jeehay28.js diff --git a/jump-game/Jeehay28.js b/jump-game/Jeehay28.js new file mode 100644 index 000000000..5f30b32b3 --- /dev/null +++ b/jump-game/Jeehay28.js @@ -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); +// }; + From 62212e2da47fb82d0de9f649ccd27ed7c5047d31 Mon Sep 17 00:00:00 2001 From: jeehay Date: Sat, 15 Feb 2025 19:43:43 +0900 Subject: [PATCH 5/6] Add merge-k-sorted-lists solution --- merge-k-sorted-lists/Jeehay28.js | 154 +++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 merge-k-sorted-lists/Jeehay28.js diff --git a/merge-k-sorted-lists/Jeehay28.js b/merge-k-sorted-lists/Jeehay28.js new file mode 100644 index 000000000..92c850eb1 --- /dev/null +++ b/merge-k-sorted-lists/Jeehay28.js @@ -0,0 +1,154 @@ +// Priority Queue (Min-Heap) - Best for larger cases +// 🕒 Time Complexity: O(N log K), where N is the total number of nodes, K is the number of lists +// 🗂️ Space Complexity: O(K) + +// +/** const PriorityQueue = require('datastructures-js').PriorityQueue; + * // This will allow you to use the heap-based approach with a priority queue, which should be more efficient than the brute-force sorting method, especially for larger input sizes. +/** + * 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 maxDepth = function (root) { + // Base case: if the node is null, the depth is 0 + if (root === null) return 0; + + // Recursively calculate the depth of the left and right subtrees + let leftDepth = maxDepth(root.left); + let rightDepth = maxDepth(root.right); + + // Return the maximum of the two depths plus 1 for the current node + return Math.max(leftDepth, rightDepth) + 1; +}; + +var mergeKLists = function (lists) { + // Create a priority queue (min-heap) to store the nodes + const pq = new PriorityQueue((a, b) => a.val - b.val); + // const pq = new PriorityQueue((a, b) => b.val - a.val); <---- max-heap + + // Initialize the priority queue with the first node from each list + for (let list of lists) { + if (list) pq.enqueue(list); // adds the element to the back (or tail) of the queue + } + + let dummy = new ListNode(-1); + let current = dummy; + + // Pop nodes from the priority queue and add them to the merged list + while (!pq.isEmpty()) { + const node = pq.dequeue(); // removes the element from the front (or head) of the queue + current.next = node; + current = current.next; + + // If the current node has a next node, enqueue it + if (node.next) pq.enqueue(node.next); + } + + return dummy.next; +}; + +/** + * ┌──────────────────────────────────────────────────────────────────────────────────────────────┐ + * │ Time and Space Complexity │ + * ├──────────────────────────────────────────────────────────────────────────────────────────────┤ + * │ Time Complexity: O(N log K) │ + * │ - N is the total number of nodes across all lists. │ + * │ - K is the number of lists. │ + * │ - Enqueueing and dequeueing each node from the priority queue takes O(log K) time. │ + * │ - Since there are N nodes in total, the overall time complexity is O(N log K). │ + * ├──────────────────────────────────────────────────────────────────────────────────────────────┤ + * │ Space Complexity: O(K) │ + * │ - The space complexity is determined by the priority queue, which stores at most K nodes. │ + * │ - Therefore, the space complexity is O(K), where K is the number of lists. │ + * ├──────────────────────────────────────────────────────────────────────────────────────────────┤ + * │ Notes: + * https://support.leetcode.com/hc/en-us/articles/360011833974-What-are-the-environments-for-the-programming-languages │ + * │ - JavaScript is run with the --harmony flag, enabling new ES6 features. │ + * │ - The lodash.js library is included by default in the environment. │ + * │ - For Priority Queue / Queue data structures, you may use: │ + * │ - datastructures-js/priority-queue version 5.4.0 │ + * │ - datastructures-js/queue version 4.2.3 │ + * │ - datastructures-js/deque version 1.04 │ + * └──────────────────────────────────────────────────────────────────────────────────────────────┘ + */ + +// 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) { + 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 │ + * └──────────────────────────────────────────────────────────────────┘ + */ From 203ebfde6d6f149e0dd118ecb00b10d377e07524 Mon Sep 17 00:00:00 2001 From: jeehay Date: Sat, 15 Feb 2025 20:19:22 +0900 Subject: [PATCH 6/6] Fix merge-k-sorted-lists solution --- merge-k-sorted-lists/Jeehay28.js | 82 -------------------------------- 1 file changed, 82 deletions(-) diff --git a/merge-k-sorted-lists/Jeehay28.js b/merge-k-sorted-lists/Jeehay28.js index 92c850eb1..4d45f3277 100644 --- a/merge-k-sorted-lists/Jeehay28.js +++ b/merge-k-sorted-lists/Jeehay28.js @@ -1,85 +1,3 @@ -// Priority Queue (Min-Heap) - Best for larger cases -// 🕒 Time Complexity: O(N log K), where N is the total number of nodes, K is the number of lists -// 🗂️ Space Complexity: O(K) - -// -/** const PriorityQueue = require('datastructures-js').PriorityQueue; - * // This will allow you to use the heap-based approach with a priority queue, which should be more efficient than the brute-force sorting method, especially for larger input sizes. -/** - * 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 maxDepth = function (root) { - // Base case: if the node is null, the depth is 0 - if (root === null) return 0; - - // Recursively calculate the depth of the left and right subtrees - let leftDepth = maxDepth(root.left); - let rightDepth = maxDepth(root.right); - - // Return the maximum of the two depths plus 1 for the current node - return Math.max(leftDepth, rightDepth) + 1; -}; - -var mergeKLists = function (lists) { - // Create a priority queue (min-heap) to store the nodes - const pq = new PriorityQueue((a, b) => a.val - b.val); - // const pq = new PriorityQueue((a, b) => b.val - a.val); <---- max-heap - - // Initialize the priority queue with the first node from each list - for (let list of lists) { - if (list) pq.enqueue(list); // adds the element to the back (or tail) of the queue - } - - let dummy = new ListNode(-1); - let current = dummy; - - // Pop nodes from the priority queue and add them to the merged list - while (!pq.isEmpty()) { - const node = pq.dequeue(); // removes the element from the front (or head) of the queue - current.next = node; - current = current.next; - - // If the current node has a next node, enqueue it - if (node.next) pq.enqueue(node.next); - } - - return dummy.next; -}; - -/** - * ┌──────────────────────────────────────────────────────────────────────────────────────────────┐ - * │ Time and Space Complexity │ - * ├──────────────────────────────────────────────────────────────────────────────────────────────┤ - * │ Time Complexity: O(N log K) │ - * │ - N is the total number of nodes across all lists. │ - * │ - K is the number of lists. │ - * │ - Enqueueing and dequeueing each node from the priority queue takes O(log K) time. │ - * │ - Since there are N nodes in total, the overall time complexity is O(N log K). │ - * ├──────────────────────────────────────────────────────────────────────────────────────────────┤ - * │ Space Complexity: O(K) │ - * │ - The space complexity is determined by the priority queue, which stores at most K nodes. │ - * │ - Therefore, the space complexity is O(K), where K is the number of lists. │ - * ├──────────────────────────────────────────────────────────────────────────────────────────────┤ - * │ Notes: - * https://support.leetcode.com/hc/en-us/articles/360011833974-What-are-the-environments-for-the-programming-languages │ - * │ - JavaScript is run with the --harmony flag, enabling new ES6 features. │ - * │ - The lodash.js library is included by default in the environment. │ - * │ - For Priority Queue / Queue data structures, you may use: │ - * │ - datastructures-js/priority-queue version 5.4.0 │ - * │ - datastructures-js/queue version 4.2.3 │ - * │ - datastructures-js/deque version 1.04 │ - * └──────────────────────────────────────────────────────────────────────────────────────────────┘ - */ - // 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)