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 09 #986

Merged
merged 5 commits into from
Feb 9, 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
49 changes: 49 additions & 0 deletions find-minimum-in-rotated-sorted-array/Jeehay28.js
Copy link
Contributor

Choose a reason for hiding this comment

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

binary search 알고리즘을 잘 사용하신것 같아요 :)

Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* @param {number[]} nums
* @return {number}
*/

// Binary Search
// Time Complexity: O(log n) (Binary search halving the search space each step)
// Space Complexity: O(1) (No extra space except for a few variables)
var findMin = function (nums) {
// Example Rotations:
// nums = [0,1,2,4,5,6,7]
// [4,5,6,7,0,1,2] -> Rotated 4 times
// [0,1,2,4,5,6,7] -> 7 times rotated

// Initial State:
// [4, 5, 6, 7, 0, 1, 2]
// ↑ ↑
// (left) (right)
//
// Find mid:
// [4, 5, 6, 7, 0, 1, 2]
// ↑ 🎯 ↑
// (left) (mid) (right)
//
// If nums[mid] > nums[right], move left to search in the right half:
// [4, 5, 6, 7, 0, 1, 2]
// ↑ ↑
// (left) (right)

let left = 0;
let right = nums.length - 1;

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

if (nums[mid] > nums[right]) {
// Minimum must be in the right half
// Need to update left to search in the right half
left = mid + 1;
} else {
// Minimum is in the left half (including mid)
// Need to update right to search in the left half
right = mid;
}
}

return nums[left];
};

39 changes: 39 additions & 0 deletions linked-list-cycle/Jeehay28.js
Copy link
Contributor

Choose a reason for hiding this comment

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

LinkedList 의 특성을 정석적으로 활용하신것 같습니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/

/**
* @param {ListNode} head
* @return {boolean}
*/

// Time Complexity: O(n)
// Space Complexity: O(1)

// - In the worst case, we might traverse the entire linked list, but because the fast pointer moves at twice the speed of the slow pointer, they will meet within O(N) steps if a cycle exists.
// - If there is no cycle, the fast pointer reaches the end in O(N) steps.
// - Only two pointers (slow and fast) are used, which require O(1) extra space.
// - No additional data structures (like arrays or hash sets) are used.

var hasCycle = function (head) {
// If there is a cycle in the linked list, Floyd's Tortoise and Hare algorithm guarantees
// that the fast and slow pointers will eventually meet.
let fast = head;
let slow = head;

while (fast && fast.next) {
slow = slow.next; // Move slow pointer one step.
fast = fast.next.next; // Move fast pointer two steps.

if (slow === fast) {
return true; // Cycle detected.
}
}

return false;
};

74 changes: 74 additions & 0 deletions maximum-product-subarray/Jeehay28.js
Copy link
Contributor

Choose a reason for hiding this comment

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

해당 문제는 DP 로 풀어야 해서 3중 for-loop를 사용하면 시간초과가 되죠!

Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* @param {number[]} nums
* @return {number}
*/

// ✅ DP approach
// Time Complexity: O(n)
// Space Complexity: O(1)
var maxProduct = function (nums) {
// Track the minimum and maximum product ending at the current position
// Consider three possibilities for each element:
// 1. Current number
// 2. Previous min product * current number (to handle negative numbers)
// 3. Previous max product * current number (to handle negative numbers)

let maxProduct = nums[0];
let previousMinProduct = 1;
let previousMaxProduct = 1;

for (const current of nums) {
const temp1 = previousMinProduct * current;
const temp2 = previousMaxProduct * current;

// Update min and max product
previousMinProduct = Math.min(current, temp1, temp2);
previousMaxProduct = Math.max(current, temp1, temp2);

// Update maxProduct
maxProduct = Math.max(maxProduct, previousMaxProduct);
}

return maxProduct;
};

// 🤔 more efficient than the previous one, but there might be a further optimization
// using dynamic programming approach to reduce the complexity to O(n)
// Time Complexity: O(n^2)
// Space Complexity: O(1)

// var maxProduct = function (nums) {
// let max = nums[0];

// for (let s = 0; s < nums.length; s++) {
// let temp = 1;
// for (let e = s; e < nums.length; e++) {
// temp *= nums[e];
// max = Math.max(max, temp);
// }
// }
// return max;
// };

// 😱 Time Limit Exceeded!
// Time Complexity: O(n^3)
// Space Complexity: O(1)
// var maxProduct = function (nums) {
// let max = nums[0];

// for (let s = 0; s < nums.length; s++) {
// for (let e = s; e < nums.length; e++) {
// let temp = 1;

// for (let i = s; i <= e; i++) {
// temp *= nums[i];
// }

// max = Math.max(max, temp);
// }
// }

// return max;
// };


63 changes: 63 additions & 0 deletions minimum-window-substring/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* @param {string} s
* @param {string} t
* @return {string}
*/

// 🚀 sliding window + two pointer approach

// 🕒 Time Complexity: O(n), where n is the length of s
// The inner while loop shrinks the window from the left side but never exceeds the total number of characters in s

// 🗂 Space Complexity: O(m) (or worst case O(n)), where n is the length of t

var minWindow = function (s, t) {
// early return for the critical edge case
if (s.length < t.length) {
return "";
}

let windowCnt = new Map();
let charCnt = new Map();
let minStart = 0;
let minEnd = s.length; // set this to an out-of-bounds value initially

let formed = 0;
let left = 0;

// initialize charCount
// 🔢 t = "ABC", charCount = { A: 1, B: 1, C: 1 }
for (const ch of t) {
charCnt.set(ch, (charCnt.get(ch) || 0) + 1);
}

// expand the windowCnt
for (let right = 0; right < s.length; right++) {
const char = s[right];

windowCnt.set(char, (windowCnt.get(char) || 0) + 1);

if (charCnt.has(char) && charCnt.get(char) === windowCnt.get(char)) {
formed += 1;
}

// shrink the window by moving the left pointer
while (formed === charCnt.size) {
if (right - left < minEnd - minStart) {
minStart = left;
minEnd = right;
}

const char = s[left];
windowCnt.set(char, windowCnt.get(char) - 1);

if (charCnt.has(char) && windowCnt.get(char) < charCnt.get(char)) {
formed -= 1;
}
left += 1;
}
}

return minEnd === s.length ? "" : s.slice(minStart, minEnd + 1);
};

111 changes: 111 additions & 0 deletions pacific-atlantic-water-flow/Jeehay28.js
Copy link
Contributor

Choose a reason for hiding this comment

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

모든 변수와 함수들이 직관적으로 정리가 매우 잘 된것 같습니다. 엄청 교과서적으로 풀이 해 주신것 같아요 대단하십니다..!

Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* @param {number[][]} heights
* @return {number[][]}
*/

// ✅ Time Complexity: O(m * n)
// ✅ Space Complexity: O(m * n)

var pacificAtlantic = function (heights) {
const m = heights.length;
const n = heights[0].length;

// ❌ Array(m).fill(Array(n).fill(false)) → Incorrect (all rows share the same array)
const pacific = Array.from({ length: m }, () => Array(n).fill(false));
const atlantic = Array.from({ length: m }, () => Array(n).fill(false));

// four possible movement directions: down, up, right, left
const directions = [
[1, 0], // ⬇️
[-1, 0], // ⬆️
[0, 1], // ➡️
[0, -1], // ⬅️
];

/**
* Depth-First Search (DFS) to mark reachable cells
* @param {number} row - current row index
* @param {number} col - current column index
* @param {boolean[][]} visited - visited cells
* @param {number} prevHeight - previously visited cell's height
*/

const dfs = (row, col, visited, prevHeight) => {
// No search needed:
// 1) Out of bounds
// 2) already visited
// 3) current height < previous height
if (
row < 0 ||
row >= m ||
col < 0 ||
col >= n ||
visited[row][col] ||
heights[row][col] < prevHeight
) {
return;
}

// mark current cell as visited
visited[row][col] = true;

// visit all four possible directions
for (const [dr, dc] of directions) {
dfs(row + dr, col + dc, visited, heights[row][col]);
}
};

// start dfs from each border
for (let i = 0; i < m; i++) {
dfs(i, 0, pacific, heights[i][0]); // left border(Pacific ocean)
dfs(i, n - 1, atlantic, heights[i][n - 1]); // right border(Atlantic ocean)
}

for (let j = 0; j < n; j++) {
dfs(0, j, pacific, heights[0][j]); // top border(Pacific ocean)
dfs(m - 1, j, atlantic, heights[m - 1][j]); // bottom border(Atlantic ocean)
}

let result = [];

for (let r = 0; r < m; r++) {
for (let c = 0; c < n; c++) {
if (pacific[r][c] && atlantic[r][c]) {
result.push([r, c]);
}
}
}
return result;
};

// Example test
const heights = [
[1, 2, 2, 3, 5],
[3, 2, 3, 4, 4],
[2, 4, 5, 3, 1],
[6, 7, 1, 4, 5],
[5, 1, 1, 2, 4],
];

const expected = [
[0, 4],
[1, 3],
[1, 4],
[2, 2],
[3, 0],
[3, 1],
[4, 0],
];

const output = pacificAtlantic(heights);

if (JSON.stringify(output.sort()) === JSON.stringify(expected.sort())) {
console.log("✅ Accepted\n");
} else {
console.log("❌ Not Accepted\n");
}

console.log("Input:", heights, "\n");
console.log("Output:", output, "\n");
console.log("Expected:", expected);