diff --git a/Sprint-1/JavaScript/calculateSumAndProduct/calculateSumAndProduct.js b/Sprint-1/JavaScript/calculateSumAndProduct/calculateSumAndProduct.js index ce738c33..e874565c 100644 --- a/Sprint-1/JavaScript/calculateSumAndProduct/calculateSumAndProduct.js +++ b/Sprint-1/JavaScript/calculateSumAndProduct/calculateSumAndProduct.js @@ -9,21 +9,26 @@ * "product": 30 // 2 * 3 * 5 * } * - * Time Complexity: - * Space Complexity: - * Optimal Time Complexity: + * Time Complexity: O(n) - one loop through the list (the original used two + * loops, 2n steps, which is also O(n)). + * + * Space Complexity: O(1) - only two variables (sum, product), regardless of + * input size + * + * Optimal Time Complexity: O(n) - every number must be read at least once, so * we can't do better than linear time + * + * Refactor: the complexity can't be reduced, but merging the two loops into + * one makes the code cleaner and does half the steps. * * @param {Array} numbers - Numbers to process * @returns {Object} Object containing running total and product */ export function calculateSumAndProduct(numbers) { let sum = 0; - for (const num of numbers) { - sum += num; - } - let product = 1; + for (const num of numbers) { + sum += num; product *= num; } diff --git a/Sprint-1/JavaScript/findCommonItems/findCommonItems.js b/Sprint-1/JavaScript/findCommonItems/findCommonItems.js index 5619ae5d..6c6dc83d 100644 --- a/Sprint-1/JavaScript/findCommonItems/findCommonItems.js +++ b/Sprint-1/JavaScript/findCommonItems/findCommonItems.js @@ -1,14 +1,41 @@ /** - * Finds common items between two arrays. - * - * Time Complexity: - * Space Complexity: - * Optimal Time Complexity: - * - * @param {Array} firstArray - First array to compare - * @param {Array} secondArray - Second array to compare - * @returns {Array} Array containing unique common items - */ -export const findCommonItems = (firstArray, secondArray) => [ - ...new Set(firstArray.filter((item) => secondArray.includes(item))), -]; +* Finds common items between two arrays. + +* Inefficiency: the original used secondArray.includes() inside firstArray +filter(), which is a loop inside a loop. + +* Originally: +* Time Complexity: O(n × m) - filter runs n times and each includes call can +* scan up to m items (O(n²) if the arrays are the same size). + +* Space Complexity: O(n) - the filtered array and the Set hold up to n items. + +* Optimal Time Complexity: O(n + m) - every item in both arrays must be read at +least once. + +* After refactor (the complexity was reduced from quadratic to linear time by +using a Set for O(1) average lookups instead of includes): + +* Time Complexity: O(n + m) - building secondSet is O(m); the loop over +firstArray is O(n) with O(1) average has() checks. + +* Space Complexity: O(n + m) - secondSet holds up to m items; commonElements +and the final Set hold up to n items. + +* Optimal Time Complexity: O(n + m) - every item in both arrays must be read at least once, so we can't do better than linear +* +* @param {Array} firstArray - First array to compare +* @param {Array} secondArray - Second array to compare +* @returns {Array} - Array containing unique common items +*/ +export const findCommonItems = (firstArray, secondArray) => { + const secondSet = new Set(secondArray); + let commonElements = []; + + for (let ele of firstArray) { + if (secondSet.has(ele)) { + commonElements.push(ele); + } + } + return [...new Set(commonElements)]; +}; diff --git a/Sprint-1/JavaScript/hasPairWithSum/hasPairWithSum.js b/Sprint-1/JavaScript/hasPairWithSum/hasPairWithSum.js index dd2901f6..a24e4f1e 100644 --- a/Sprint-1/JavaScript/hasPairWithSum/hasPairWithSum.js +++ b/Sprint-1/JavaScript/hasPairWithSum/hasPairWithSum.js @@ -1,21 +1,43 @@ /** * Find if there is a pair of numbers that sum to a given target value. * - * Time Complexity: - * Space Complexity: - * Optimal Time Complexity: + * Inefficiency: the original used a loop inside a loop to check every + * possible pair of numbers. + * + * Originally: + * Time Complexity: O(n²) - for each number, the inner loop scans the + * remaining numbers, so about n * (n - 1) / 2 pair checks. + * + * Space Complexity: O(1) - only the loop counters i and j are used. + * + * Optimal Time Complexity: O(n) - every number must be read at least once, + * and a Set gives O(1) average lookups, so one pass is enough + * + * After Refactor (reduced from quadratic to linear time by remembering the + * numbers already seen in a Set instead of re-scanning the list): + * + * Time Complexity: O(n) - one loop, with O(1) average has() and add(). + * + * Space Complexity: O(n) - visitedNumbers can hold up to n numbers. + * + * Optimal Time Complexity: O(n) - every number must be read at least once, + * so we can't do better than linear. * * @param {Array} numbers - Array of numbers to search through * @param {number} target - Target sum to find * @returns {boolean} True if pair exists, false otherwise */ export function hasPairWithSum(numbers, target) { - for (let i = 0; i < numbers.length; i++) { - for (let j = i + 1; j < numbers.length; j++) { - if (numbers[i] + numbers[j] === target) { - return true; - } + const visitedNumbers = new Set(); + + for (let num of numbers) { + const partnerNum = target - num; + + if (visitedNumbers.has(partnerNum)) { + return true; } + visitedNumbers.add(num); } + return false; } diff --git a/Sprint-1/JavaScript/removeDuplicates/removeDuplicates.mjs b/Sprint-1/JavaScript/removeDuplicates/removeDuplicates.mjs index dc5f7711..68feb30b 100644 --- a/Sprint-1/JavaScript/removeDuplicates/removeDuplicates.mjs +++ b/Sprint-1/JavaScript/removeDuplicates/removeDuplicates.mjs @@ -1,36 +1,29 @@ /** - * Remove duplicate values from a sequence, preserving the order of the first occurrence of each value. - * - * Time Complexity: - * Space Complexity: - * Optimal Time Complexity: - * - * @param {Array} inputSequence - Sequence to remove duplicates from - * @returns {Array} New sequence with duplicates removed - */ -export function removeDuplicates(inputSequence) { - const uniqueItems = []; - - for ( - let currentIndex = 0; - currentIndex < inputSequence.length; - currentIndex++ - ) { - let isDuplicate = false; - for ( - let compareIndex = 0; - compareIndex < uniqueItems.length; - compareIndex++ - ) { - if (inputSequence[currentIndex] === uniqueItems[compareIndex]) { - isDuplicate = true; - break; - } - } - if (!isDuplicate) { - uniqueItems.push(inputSequence[currentIndex]); - } - } +* Remove duplicate values from a sequence, preserving the order of the first +* occurrence of each value. +* +* Originally (nested loops): +* Time Complexity: O(n²) - the outer loop runs n times and the inner loop +* scans uniqueItems, which can grow up to n items (about n(n - 1) / 2 +* checks in the worst case, when all items are unique). +* +* Space Complexity: O(n) - uniqueItems can hold up to n items. +* +* Optimal Time Complexity: O(n) - every item must be read at least once, and +* a Set gives O(1) average lookups. - return uniqueItems; +* After refactor ([...new Set(inputSequence)]): +* Time Complexity: O(n) - building the Set visits each item once with O(1) +* average inserts; spreading it into an array is also O(n). +* +* Space Complexity: O(n) - the Set and the result array each hold up to n items. +* +* Optimal Time Complexity: O(n) - every item must be read at least once, so we +* can't do better than linear. +* +* @param {Array} inputSequence - Sequence to remove duplicates from +* @returns {Array} New sequence with duplicates removed +*/ +export function removeDuplicates(inputSequence) { + return [...new Set(inputSequence)]; }