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 05 #858

Merged
merged 4 commits into from
Jan 12, 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
41 changes: 41 additions & 0 deletions best-time-to-buy-and-sell-stock/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* @param {number[]} prices
* @return {number}
*/

// TC : O(n)
// SC : O(1)

var maxProfit = function (prices) {
if (prices.length === 1) {
return 0;
}

// Two variables (profitMax and priceMin) are used to store the maximum profit and minimum price seen, which require O(1) space.
let profitMax = 0;
let priceMin = prices[0];

for (const price of prices) {
const profit = price - priceMin;
profitMax = Math.max(profit, profitMax);
priceMin = Math.min(price, priceMin);
}

return profitMax;
};

// Why Constants Are Ignored in Big-O
ekgns33 marked this conversation as resolved.
Show resolved Hide resolved
// In Big-O notation, O(2) is simplified to O(1) because constants are irrelevant in asymptotic analysis.
// Big-O focuses on how resource usage scales with input size, not fixed values.

// Using 2 variables: O(1)
// Using 10 variables: O(1)
// Using 100 variables: O(1)

// What Space Complexity Looks Like for Larger Growth
// O(n): Memory grows linearly with the input size (e.g., storing an array of n elements).
// O(n^2): Memory grows quadratically (e.g., a 2D matrix with n*n elements).
// 𝑂(log 𝑛): Memory grows logarithmically (e.g., recursive calls in binary search).
// O(1): Fixed memory usage, regardless of input size (e.g., using a fixed number of variables).


88 changes: 88 additions & 0 deletions group-anagrams/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Guided approach
// TC : O(n*k), where n is the number of strings, and k is the average length of each string.
// SC : O(n*k)
// overal time complexity improved : from O(n * klogk) to O(n * k)

/**
* Time Complexity Breakdown:
*
* Step | Time Complexity | Explanation
* --------------------------------------- | ------------------- | ----------------------------------------
* Outer loop over strings (`for` loop) | O(n) | Iterate over each string in the input array `strs`.
* Create key (`createKey`) | O(k) per string | For each string, count character frequencies, with k being the length of the string.
* Map operations (`set` and `get`) | O(1) per string | Inserting and retrieving values from a Map.
* Result array | O(n * k) | Storing grouped anagrams in the result array.
*
* Overall Time Complexity: | O(n * k) | Total time complexity considering all steps.
*
* Space Complexity Breakdown:
*
* Step | Space Complexity | Explanation
* --------------------------------------- | ------------------- | -----------------------------------------
* Map to store grouped anagrams | O(n * k) | Map stores n groups with each group having at most k characters.
* Auxiliary space for `createKey` | O(1) | The frequency array used to count characters (constant size of 26).
* Space for the result array | O(n * k) | Result array storing n groups of up to k elements.
*
* Overall Space Complexity: | O(n * k) | Total space complexity considering all storage.
*/

/**
* @param {string[]} strs
* @return {string[][]}
*/

var groupAnagrams = function (strs) {
const createKey = (str) => {
const arr = new Array(26).fill(0);

for (const ch of str) {
const idx = ch.charCodeAt() - "a".charCodeAt();
ekgns33 marked this conversation as resolved.
Show resolved Hide resolved
arr[idx] += 1;
}

return arr.join("#");
};

let map = new Map();

for (const str of strs) {
const key = createKey(str);
map.set(key, [...(map.get(key) || []), str]);
}

return Array.from(map.values(map));
};

// *My own approach

// Time Complexity
// 1. Sorting Each String:
// Sorting a string takes O(k*logk), where k is the length of the string.
// Since we sort each string in the input array of size n, the total cost for sorting is O(n*klogk).

// 2. Hash Map Operations:
// Inserting into the hash map is O(1) on average. Over n strings, the cost remains O(n).

// Overall Time Complexity:
// O(n*klogk), where n is the number of strings and k is the average length of a string.

// /**
// * @param {string[]} strs
// * @return {string[][]}
// */

// var groupAnagrams = function (strs) {
// // helper function
// const sorted = (str) => {
// return str.split("").sort().join("");
// };

// let obj = {};

// for (const str of strs) {
// const key = sorted(str);
ekgns33 marked this conversation as resolved.
Show resolved Hide resolved
// obj[key] = [...(obj[key] || []), str];
// }

// return Object.values(obj);
// };
69 changes: 69 additions & 0 deletions implement-trie-prefix-tree/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Space complexity: O(n * m), where n is the number of words and m is the average length of the words stored in the trie.
var Trie = function () {
this.root = {}; // Initialize the trie with a root node
};

/**
* @param {string} word
* @return {void}
*/

// Time Complexity: O(m), where m is the length of the word being inserted
Trie.prototype.insert = function (word) {
let currentNode = this.root;
for (any of word) {
// If the character doesn't exist, create a new node
if (!currentNode[any]) {
currentNode[any] = {};
}
currentNode = currentNode[any]; // Move to the next node
}
currentNode.end = true; // Mark the end of the word
};

/**
* @param {string} word
* @return {boolean}
*/
// Time Complexity: O(m), where m is the length of the word being searched
Trie.prototype.search = function (word) {
let currentNode = this.root;
for (any of word) {
// If the character doesn't exist in the trie, return false
if (!currentNode[any]) {
return false;
}
currentNode = currentNode[any]; // Move to the next node
}

return currentNode.end === true;
};

/**
* @param {string} prefix
* @return {boolean}
*/
// Time Complexity: O(m), where m is the length of the prefix
Trie.prototype.startsWith = function (prefix) {
let currentNode = this.root;

for (any of prefix) {
// If the character doesn't exist, return false
if (!currentNode[any]) {
return false;
}
currentNode = currentNode[any]; // Move to the next node
}

return true; // Return true if the prefix exists
};

/**
* Your Trie object will be instantiated and called as such:
* var obj = new Trie()
* obj.insert(word)
* var param_2 = obj.search(word)
* var param_3 = obj.startsWith(prefix)
*/


36 changes: 36 additions & 0 deletions word-break/Jeehay28.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* @param {string} s
* @param {string[]} wordDict
* @return {boolean}
*/

// Time Complexity: O(n * w * m)
// - n is the length of the string s.
// - w is the number of words in the dictionary wordDict.
// - m is the average length of words in wordDict.

// Space Complexity: O(n)
// - The dp array of size n + 1 is the primary contributor to space usage, where n is the length of the string s.
var wordBreak = function (s, wordDict) {
dp = new Array(s.length + 1).fill(false);
dp[0] = true;

// O(n)
for (let i = 1; i <= s.length; i++) {
// O(w)
for (word of wordDict) {
if (i >= word.length && s.slice(i - word.length, i) === word) {
// s.slice(i - word.length, i), the slicing operation takes O(m), where m is the length of the word being checked
dp[i] = dp[i - word.length];
}

if (dp[i]) {
break;
}
}
}

return dp[s.length];
};


Loading