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 07 #933

Merged
merged 6 commits into from
Jan 25, 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
64 changes: 64 additions & 0 deletions longest-substring-without-repeating-characters/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* @param {string} s
* @return {number}
*/

// 🌟 sliding window technique

// Time Complexity: O(n)
// Why it's O(n)
// - The end pointer iterates over the string exactly once (O(n)).
// - The start pointer also only moves forward without re-processing elements (O(n)).
// - Therefore, the total amount of work done is proportional to the length of the string (n).
// - So, even though there is a while loop inside the for loop, the total work done (number of operations) is still linear, O(n), because the start and end pointers together move across the string just once.
// - This is the key reason why the time complexity is O(n), even with nested loops.

// Space Complexity: O(k), where k k is the length of the longest substring without repeating characters.
// In the worst case, k = n, so O(n)

var lengthOfLongestSubstring = function (s) {
let start = 0;
let longest = 0;
let subString = new Set();

for (let end = 0; end < s.length; end++) {
while (subString.has(s[end])) {
// Shrink the window by moving start
subString.delete(s[start]);
start += 1;
}

subString.add(s[end]);
longest = Math.max(longest, end - start + 1);
}

return longest;
};

// 🛠️ Solution 1
// Time Complexity: O(n^2), where n is the length of the string s
// Space Complexity: O(k), where k is the length of the longest substring without repeating characters (k ≤ n)

// why the space complexity is not just O(n):
// - Saying O(n) is technically correct in the worst case,
// - but it hides the fact that the actual space usage is proportional to the length of the longest substring without repeats,
// - which could be much smaller than n in many practical cases (e.g., for a string like "aaabbbccc").

// var lengthOfLongestSubstring = function (s) {
// let longest = 0;

// for (let i = 0; i < s.length; i++) {
// let subString = new Set();

// for (let j = i; j < s.length; j++) {
// if (subString.has(s[j])) {
// break;
// } else {
// subString.add(s[j]);
// longest = Math.max(longest, j - i + 1);
// }
// }
// }
// return longest;
// };

50 changes: 50 additions & 0 deletions number-of-islands/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* @param {character[][]} grid
* @return {number}
*/

// 🌻
// Time Complexity: O(m * n), where M is the number of rows and N is the number of columns in the grid.
// Space Complexity: O(k), where k is the size of the largest island (k <= m * n)

// The space complexity is determined by the depth of the recursive stack used by the sink function.
// In the worst-case scenario, where the entire grid is filled with "1"s (a single large island),
// the depth of recursion could go up to O(m * n).

var numIslands = function (grid) {
// depth-first search (DFS) that potentially visits every connected cell in the current island
const sink = (row, col) => {
grid[row][col] = "0";

const adjacent = [
[row - 1, col], // up
[row + 1, col], // down
[row, col - 1], // left
[row, col + 1], // right
];

for ([r, c] of adjacent) {
if (r >= 0 && r < grid.length && c >= 0 && c < grid[r].length) {
if (grid[r][c] === "1") {
sink(r, c);
}
}
}
};

let cnt = 0;

for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) {
if (grid[i][j] === "1") {
cnt += 1;
sink(i, j);
}
}
}

return cnt;
};



82 changes: 82 additions & 0 deletions reverse-linked-list/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/

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

// The algorithm uses a constant amount of extra space: prev, current, and nextTemp.
// No additional data structures (like arrays or new linked lists) are created.
// Hence, the space complexity is O(1).

var reverseList = function (head) {
let prev = null;
let current = head;

while (current) {
let nextTemp = current.next;

current.next = prev;
console.log(current, prev);

prev = current;
console.log(current, prev);
current = nextTemp;
}

return prev; // New head of the reversed list
};

// head = [1,2,3,4,5]
// [1] null
// [1] [1]
// [2,1] [1]
// [2,1] [2,1]
// [3,2,1] [2,1]
// [3,2,1] [3,2,1]
// [4,3,2,1] [3,2,1]
// [4,3,2,1] [4,3,2,1]
// [5,4,3,2,1] [5,4,3,2,1]

// my own approach
// Time Complexity: O(n)
// Space Complexity: O(n)
var reverseList = function (head) {
if (head === null) {
return null;
}

let current = head;
let arr = [];
// console.log(head.val)

// traverse the linked List - TC: O(n)
while (current) {
arr.push(current.val);
current = current.next;
}

// reverse the array - TC: O(n)
arr = arr.reverse();

let head1 = new ListNode(arr[0]);
let current1 = head1;

// rebuild the linked list - TC: O(n)
for (let i = 1; i < arr.length; i++) {
current1.next = new ListNode(arr[i]);
current1 = current1.next;
}

return head1;
};


106 changes: 106 additions & 0 deletions set-matrix-zeroes/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// 🌈 In-Place Solution (Without extra space)
// The term "in-place" refers to algorithms or solutions that modify the input data directly,
// without needing extra space for a separate copy of the data. In an in-place solution,
// the operations are performed using a fixed amount of extra memory, typically O(1) space, beyond the input data itself.

/**
* @param {number[][]} matrix
* @return {void} Do not return anything, modify matrix in-place instead.
*/

// Time Complexity: O(m * n)
// Space Complexity: O(1)
var setZeroes = function (matrix) {
const rows = matrix.length;
const cols = matrix[0].length;

let firstRowHasZero = false;
let firstColHasZero = false;

// Check if the first row has any zero
for (let c = 0; c < cols; c++) {
if (matrix[0][c] === 0) {
firstRowHasZero = true;
break;
}
}

// Check if the first column has any zero
for (let r = 0; r < rows; r++) {
if (matrix[r][0] === 0) {
firstColHasZero = true;
break;
}
}

// Use first row and column to mark zeros
for (let i = 1; i < rows; i++) {
for (let j = 1; j < cols; j++) {
if (matrix[i][j] === 0) {
matrix[0][j] = 0;
matrix[i][0] = 0;
}
}
}

// Set zeros based on marks in the first row and column
for (let i = 1; i < rows; i++) {
for (let j = 1; j < cols; j++) {
if (matrix[0][j] === 0 || matrix[i][0] === 0) {
matrix[i][j] = 0;
}
}
}

// Handle first row
if (firstRowHasZero) {
for (let c = 0; c < cols; c++) {
matrix[0][c] = 0;
}
}

// Handle first column
if (firstColHasZero) {
for (let r = 0; r < rows; r++) {
matrix[r][0] = 0;
}
}

return matrix;
};

/**
* @param {number[][]} matrix
* @return {void} Do not return anything, modify matrix in-place instead.
*/

// 💪 My initial approach with Set...
// Time Complexity: O(m * n)
// Space Complexity: O(m + n)
// var setZeroes = function (matrix) {
// let rows = new Set();
// let cols = new Set();

// for (let i = 0; i < matrix.length; i++) {
// for (let j = 0; j < matrix[0].length; j++) {
// if (matrix[i][j] === 0) {
// rows.add(i);
// cols.add(j);
// }
// }
// }

// for (row of rows) {
// matrix[row] = new Array(matrix[0].length).fill(0);
// }

// for (col of cols) {
// for (let row = 0; row < matrix.length; row++) {
// matrix[row][col] = 0;
// }
// }

// return matrix;
// };


86 changes: 86 additions & 0 deletions unique-paths/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* @param {number} m
* @param {number} n
* @return {number}
*/

// 🍎 DP
// Time Complexity: O(m*n)
// Space Complexity: O(n)

// Row 0: [1, 1, 1, 1]
// Row 1: [1, 2, 3, 4]
// Row 2: [1, 3, 6, 10]

// Initial dp: [1, 1, 1, 1]
// left = 1 (always starts with 1 for the first column)

// col = 1: left += dp[1] → left = 1 + 1 = 2 → dp[1] = left
// col = 2: left += dp[2] → left = 2 + 1 = 3 → dp[2] = left
// col = 3: left += dp[3] → left = 3 + 1 = 4 → dp[3] = left

// dp after row 1: [1, 2, 3, 4]

// Initial dp: [1, 2, 3, 4]
// left = 1

// col = 1: left += dp[1] → left = 1 + 2 = 3 → dp[1] = left
// col = 2: left += dp[2] → left = 3 + 3 = 6 → dp[2] = left
// col = 3: left += dp[3] → left = 6 + 4 = 10 → dp[3] = left

// dp after row 2: [1, 3, 6, 10]

var uniquePaths = function (m, n) {
let dp = new Array(n).fill(1);

for (let row = 1; row < m; row++) {
let left = 1;
for (let col = 1; col < n; col++) {
left += dp[col];
dp[col] = left;
}
}

return dp[n - 1];
};

/**
* @param {number} m
* @param {number} n
* @return {number}
*/

// 🍎 DFS
// Time Complexity: O(m*n)
// Space Complexity: O(m*n)

// var uniquePaths = function (m, n) {
// const memo = {};

// const dfs = (row, col) => {
// if (row === m - 1 && col === n - 1) {
// return 1;
// }

// let cnt = 0;

// const key = `${row}, ${col}`;
// if (key in memo) {
// return memo[key];
// }

// if (row + 1 < m) {
// cnt += dfs(row + 1, col);
// }

// if (col + 1 < n) {
// cnt += dfs(row, col + 1);
// }

// memo[key] = cnt;

// return cnt;
// };

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