diff --git a/Sprint-1/JavaScript/calculateSumAndProduct/calculateSumAndProduct.js b/Sprint-1/JavaScript/calculateSumAndProduct/calculateSumAndProduct.js index ce738c3..8f72536 100644 --- a/Sprint-1/JavaScript/calculateSumAndProduct/calculateSumAndProduct.js +++ b/Sprint-1/JavaScript/calculateSumAndProduct/calculateSumAndProduct.js @@ -18,17 +18,22 @@ */ export function calculateSumAndProduct(numbers) { let sum = 0; - for (const num of numbers) { - sum += num; - } - let product = 1; - for (const num of numbers) { - product *= num; + for (let i = 0; i < numbers.length; i++) { + const item = numbers[i]; + sum += item; + product *= item; } - return { - sum: sum, - product: product, - }; + return { sum, product }; } +// before we had two for loops , imagine numbers is a array of 1,000,000 numbers, then with two for loops it would run 2 milions time +// now we have one for loop and it will do it one milion times which is half of that + +// cleaner and simpler return which does exactly same as before return + +// the traditional for loop is faster and simpler for computers + +/** Time Complexity (Original): O(N) — The original code uses two separate, back-to-back loops. It loops through the list once for the sum, and then loops through the list a second time for the product. + * Space Complexity (Improved): O(1) — The improved version uses constant memory. It only stores a few simple number variables (sum, product, and i), which takes up almost no space regardless of how big the list grows. + * Optimal Time Complexity (Improved): O(N) — The improved version is still linear time, but it is slightly better because it combines everything into a single loop. It does all the math in just one pass through the list. */ \ No newline at end of file diff --git a/Sprint-1/JavaScript/findCommonItems/findCommonItems.js b/Sprint-1/JavaScript/findCommonItems/findCommonItems.js index 5619ae5..79f6266 100644 --- a/Sprint-1/JavaScript/findCommonItems/findCommonItems.js +++ b/Sprint-1/JavaScript/findCommonItems/findCommonItems.js @@ -9,6 +9,24 @@ * @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))), -]; +export const findCommonItems = (firstArray, secondArray) => { + if (!firstArray || !secondArray) return []; + + // remove all duplicates + const firstSet = new Set(firstArray); + const secondSet = new Set(secondArray); + + const common = [...firstSet.intersection(secondSet)]; + + return common; +}; +// a normal list can have same item many time +// we use new Set to remove dublicates for both arrays +// because we cleaned firat array , the computer never check same word twice, same for second array +// filter works with normal arrays so we spred firstSet to look like array +// we put if statement if eaither one is empty it doesnt crash +/** + * Time Complexity (Original): O(N * M) — The original code used .filter() on the first list and .includes() on the second list. Because .includes() has to look through the second list one item at a time from scratch, it acts like a hidden nested loop. For big lists, this gets incredibly slow. + * Space Complexity (Improved): O(N + M) — The improved version uses extra memory because it creates two new sets (firstSet and secondSet) to hold the unique items from both lists. + * Optimal Time Complexity (Improved): O(N + M) — By turning the second list into a Set, the .has() check becomes instant O(1) instead of scanning the whole list. This makes the entire function run in fast linear time. + */ \ No newline at end of file diff --git a/Sprint-1/JavaScript/hasPairWithSum/hasPairWithSum.js b/Sprint-1/JavaScript/hasPairWithSum/hasPairWithSum.js index dd2901f..bbd398e 100644 --- a/Sprint-1/JavaScript/hasPairWithSum/hasPairWithSum.js +++ b/Sprint-1/JavaScript/hasPairWithSum/hasPairWithSum.js @@ -10,12 +10,31 @@ * @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; - } + // we start with an empty box called seenNumbers to remember what we look at + // before with two loops, an array of 1,000 numbers would take 1 milion steps + // Now, with this box, we only need to look at each number exactly one time. + const seenNumbers = new Set(); + // This loop goes through the list of numbers just one single time + for (const num of numbers) { + // get the "missing partner" number we need to hit the target sum + const complement = target - num; + // We check our box. Is the missing partner already inside? + // Checking this box is super fast for computers + if (seenNumbers.has(complement)) { + // if the partner is in the box, we found a matching pair , we sent true + return true; } + // if the partner isn't in the box yet, put the current number in the box so we remember it for later + seenNumbers.add(num); } + return false; } + + +/** + * Time Complexity (Original): O(N^2) — The original code used a loop inside a loop (i and j). Because it had to compare every single number with every other number, it was very slow and took a massive number of steps for large datasets. + * Space Complexity (Improved): O(N) — The improved version uses extra memory for the seenNumbers set so it can remember the numbers it looks at. + * Optimal Time Complexity (Improved): O(N) — By using the set, the code now only loops through the list exactly one time, making the lookup instant and dropping the time significantly. + */ + diff --git a/Sprint-1/JavaScript/removeDuplicates/removeDuplicates.mjs b/Sprint-1/JavaScript/removeDuplicates/removeDuplicates.mjs index dc5f771..8cb4870 100644 --- a/Sprint-1/JavaScript/removeDuplicates/removeDuplicates.mjs +++ b/Sprint-1/JavaScript/removeDuplicates/removeDuplicates.mjs @@ -9,28 +9,13 @@ * @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]); - } - } - - return uniqueItems; + // put items in a Set to remove duplicates, then make it back into an array + return [...new Set(inputSequence)]; } + +// the old code uses a loop inside a loop. Because it multiplies the work, it gets slower and slower the bigger your list grows +/** + * Time Complexity (Original): O(N^2) — The original code used a loop inside a loop (currentIndex and compareIndex) to scan through the growing uniqueItems list for every item. This made it very slow for larger inputs. + * Space Complexity (Improved): O(N) — The improved version uses extra memory to create a temporary Set and build the new unique array. + * Optimal Time Complexity (Improved): O(N) — By using a Set, the computer instantly filters out all duplicates in a single pass through the data, making it incredibly fast. + */ diff --git a/Sprint-1/Python/calculate_sum_and_product/calculate_sum_and_product.py b/Sprint-1/Python/calculate_sum_and_product/calculate_sum_and_product.py index cfd5cfd..61f6fa9 100644 --- a/Sprint-1/Python/calculate_sum_and_product/calculate_sum_and_product.py +++ b/Sprint-1/Python/calculate_sum_and_product/calculate_sum_and_product.py @@ -20,12 +20,21 @@ def calculate_sum_and_product(input_numbers: List[int]) -> Dict[str, int]: if not input_numbers: return {"sum": 0, "product": 1} - sum = 0 + total_sum = 0 + total_product = 1 for current_number in input_numbers: - sum += current_number + total_sum += current_number + total_product *= current_number - product = 1 - for current_number in input_numbers: - product *= current_number + return {"sum": total_sum, "product": total_product} + +# in the old code we have two separate loops that run one after the other. If the list has 10,000 numbers, the first loop runs 10,000 times to calculate the sum, and the second loop runs 10,000 times to calculate the product +# in the new code we use only one loop which will be doing same job by working half of the old code +# if input_numbers is a small array you may dont see a diffirent but if it is a massive array then you will see it is much faster +# sum is a built-in function name, it is safer to use a name like total_sum so Python doesnt get confused - return {"sum": sum, "product": product} +""" +Time Complexity (Original): O(N) — The original code is fast because it uses two separate loops that run one after the other. It loops through the list once for the sum, and then loops through it a second time for the product. +Space Complexity (Improved): O(1) — The improved version uses constant memory. It only tracks a couple of simple numbers (total_sum and total_product), which takes up the same tiny amount of space no matter how big the list grows. +Optimal Time Complexity (Improved): O(N) — The improved version is still linear time, but it is better because it combines everything into a single loop. It does all the math in just one single pass through the list. +""" diff --git a/Sprint-1/Python/find_common_items/find_common_items.py b/Sprint-1/Python/find_common_items/find_common_items.py index 478e2ef..fdcf4a3 100644 --- a/Sprint-1/Python/find_common_items/find_common_items.py +++ b/Sprint-1/Python/find_common_items/find_common_items.py @@ -13,9 +13,23 @@ def find_common_items( Space Complexity: Optimal time complexity: """ - common_items: List[ItemType] = [] - for i in first_sequence: - for j in second_sequence: - if i == j and i not in common_items: - common_items.append(i) - return common_items + if not first_sequence or not second_sequence: + return [] + + result_set = set(first_sequence) & set(second_sequence) + + return list(result_set) + +# old one have a loop inside a loop for i and for j. If both lists have 10,000 items, that is like 100,000,000 checks + +""" + this function turns both lists into "Sets" to remove duplicates + then, it checks each item to find + the common elements without using slow nested loops. + """ + +""" +Time Complexity (Original): O(N \times M) — The original code used a loop inside a loop (for i and for j). To make it even slower, it also used i not in common_items, which forced the computer to scan a third list. This takes a massive number of steps for large datasets. +Space Complexity (Improved): O(N + M) — The improved version uses extra memory to store the unique items from both sequences inside sets (set_first, set_two, and result_set). +Optimal Time Complexity (Improved): O(N + M) — By turning the sequences into sets, looking up an item with if item in set_two becomes instant. This allows the function to find all common items in a single, fast pass. +""" \ No newline at end of file diff --git a/Sprint-1/Python/has_pair_with_sum/has_pair_with_sum.py b/Sprint-1/Python/has_pair_with_sum/has_pair_with_sum.py index fe2da51..24be381 100644 --- a/Sprint-1/Python/has_pair_with_sum/has_pair_with_sum.py +++ b/Sprint-1/Python/has_pair_with_sum/has_pair_with_sum.py @@ -11,8 +11,28 @@ def has_pair_with_sum(numbers: List[Number], target_sum: Number) -> bool: Space Complexity: Optimal time complexity: """ - for i in range(len(numbers)): - for j in range(i + 1, len(numbers)): - if numbers[i] + numbers[j] == target_sum: - return True + seen_numbers = set() + + for current_number in numbers: + needed_partner = target_sum - current_number + + if needed_partner in seen_numbers: + return True + + seen_numbers.add(current_number) + return False + +#because old code have a nested loop (a loop inside a loop), itsmatching every number against every other single number in the list +""" + new function go through the list just once, for each number, + it finds the exact "matching partner" needed to reach the target. + It saves everything it sees into a Set so it can check for that partner + completely avoiding slow nested loops. + """ + +""" +Time Complexity (Original): O(N^2) — The original code used a loop inside a loop (i and j). Because it had to manually check every possible combination of numbers, it became incredibly slow when dealing with large lists. +Space Complexity (Improved): O(N) — The improved version uses extra memory to store the numbers inside the seen_numbers set as it loops through them. +Optimal Time Complexity (Improved): O(N) — By using a set, checking if needed_partner in seen_numbers is instant. The code now only needs to look at each number in the list exactly one time, making it lightning-fast. +""" diff --git a/Sprint-1/Python/remove_duplicates/remove_duplicates.py b/Sprint-1/Python/remove_duplicates/remove_duplicates.py index c9fdbe8..95313c2 100644 --- a/Sprint-1/Python/remove_duplicates/remove_duplicates.py +++ b/Sprint-1/Python/remove_duplicates/remove_duplicates.py @@ -11,15 +11,19 @@ def remove_duplicates(values: Sequence[ItemType]) -> List[ItemType]: Space complexity: Optimal time complexity: """ - unique_items = [] - for value in values: - is_duplicate = False - for existing in unique_items: - if value == existing: - is_duplicate = True - break - if not is_duplicate: - unique_items.append(value) + return list(dict.fromkeys(values)) - return unique_items +#old code hs a nested loop: for every single value in the original list, you loop through its unique_items list to see if it is already there. For a massive list of 100,000 items, this turns into billions of checks, making the program very slow +""" + new function does through the list just once. It uses a Set as a + quick memory notepad to check if an item is a duplicate. + If it's a brand new item, it saves it to the Set and adds it to + the result List, keeps everything in original order. + """ + +""" +Time Complexity (Original): O(N^2) — The original code used a loop inside a loop (for value and for existing). For every single item in the original list, it had to scan through the unique_items list one-by-one to check for copies, which makes it very slow for big data. +Space Complexity (Improved): O(N) — The improved version uses extra memory to store items inside both a seen set and a unique_items list. +Optimal Time Complexity (Improved): O(N) — By using a set, checking if value not in seen is instant. The computer now flattens out all duplicates in just one single pass while perfectly keeping the original order of the items. +"""