Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,36 @@
* and the "product" is every number multiplied together
* so for example: [2, 3, 5] would return
* {
* "sum": 10, // 2 + 3 + 5
* "product": 30 // 2 * 3 * 5
* "sum": 10,
* "product": 30
* }
*
* Time Complexity:
* Space Complexity:
* Optimal Time Complexity:
* Time Complexity: O(n)
* The function loops through the array once, processing each element exactly once.
*
* Space Complexity: O(1)
* Only two variables (sum and product) are used, regardless of the input size.
*
* Optimal Time Complexity: O(n)
* This is the optimal time complexity because every element must be read at least
* once to calculate both the sum and the product.
*
* @param {Array<number>} 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 sum = 0
let product = 1

let product = 1;
// Refactored to use a single loop instead of two.
// This reduces the number of iterations but keeps the time complexity at O(n).
for (const num of numbers) {
product *= num;
sum += num
product *= num
}

return {
sum: sum,
product: product,
};
sum,
product,
}
}
15 changes: 11 additions & 4 deletions Sprint-1/JavaScript/findCommonItems/findCommonItems.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
/**
* Finds common items between two arrays.
*
* Time Complexity:
* Space Complexity:
* Optimal Time Complexity:
* Time Complexity: O(n × m)
* The function loops through the first array and uses `includes()` to search
* the second array for each element.
*
* Space Complexity: O(n)
* A new array is created to store the matching items.
*
* Optimal Time Complexity: O(n × m)
* The complexity cannot be reduced without using an additional lookup
* data structure such as a Set or a hash map.
*
* @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))),
];
]
26 changes: 17 additions & 9 deletions Sprint-1/JavaScript/hasPairWithSum/hasPairWithSum.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
/**
* Find if there is a pair of numbers that sum to a given target value.
*
* Time Complexity:
* Space Complexity:
* Optimal Time Complexity:
* Original Time Complexity: O(n²)
* The original code uses nested loops to compare every possible pair of numbers.
*
* Space Complexity: O(n)
* A Set is used to store numbers that have already been seen.
*
* Optimal Time Complexity: O(n)
* The array is traversed once, and Set lookups are O(1) on average.
*
* @param {Array<number>} 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 seen = new Set()

for (const num of numbers) {
if (seen.has(target - num)) {
return true
}

seen.add(num)
}
return false;

return false
}
43 changes: 13 additions & 30 deletions Sprint-1/JavaScript/removeDuplicates/removeDuplicates.mjs
Original file line number Diff line number Diff line change
@@ -1,36 +1,19 @@
/**
* Remove duplicate values from a sequence, preserving the order of the first occurrence of each value.
* Remove duplicate items from a sequence.
*
* Time Complexity:
* Space Complexity:
* Optimal Time Complexity:
* Original Time Complexity: O(n²)
* The original code uses a nested loop to compare every item with the
* previously collected unique items.
*
* @param {Array} inputSequence - Sequence to remove duplicates from
* @returns {Array} New sequence with duplicates removed
* Space Complexity: O(n)
* A new collection is created to store unique items.
*
* Optimal Time Complexity: O(n)
* A Set allows constant-time lookups, so each item only needs to be checked once.
*
* @param {Array} inputSequence - Sequence containing possible duplicates
* @returns {Array} Sequence containing only unique items
*/
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;
return [...new Set(inputSequence)]
}
42 changes: 42 additions & 0 deletions Sprint-2/implement_linked_list/linked_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
class Node:
def __init__(self, value):
self.value = value
self.next = None
self.previous = None


class LinkedList:
def __init__(self):
self.head = None
self.tail = None

def push_head(self, value):
node = Node(value)
node.next = self.head

if self.head is not None:
self.head.previous = node
else:
self.tail = node

self.head = node
return node

def pop_tail(self):
node = self.tail
self.remove(node)
return node.value

def remove(self, node):
if node.previous is not None:
node.previous.next = node.next
else:
self.head = node.next

if node.next is not None:
node.next.previous = node.previous
else:
self.tail = node.previous

node.next = None
node.previous = None
60 changes: 60 additions & 0 deletions Sprint-2/implement_lru_cache/lru_cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class Node:
def __init__(self, key=None, value=None):
self.key = key
self.value = value
self.next = None
self.previous = None


class LruCache:
def __init__(self, limit):
if limit <= 0:
raise ValueError("limit must be positive")

self.limit = limit
self.entries = {}

# Sentinel nodes bound the list so add/remove never special-case
# empty lists or the ends of the list.
self.head = Node()
self.tail = Node()
self.head.next = self.tail
self.tail.previous = self.head

def get(self, key):
node = self.entries.get(key)
if node is None:
return None

self._move_to_front(node)
return node.value

def set(self, key, value):
node = self.entries.get(key)
if node is not None:
node.value = value
self._move_to_front(node)
return

node = Node(key, value)
self.entries[key] = node
self._add_to_front(node)

if len(self.entries) > self.limit:
lru = self.tail.previous
self._remove(lru)
del self.entries[lru.key]

def _move_to_front(self, node):
self._remove(node)
self._add_to_front(node)

def _add_to_front(self, node):
node.next = self.head.next
node.previous = self.head
self.head.next.previous = node
self.head.next = node

def _remove(self, node):
node.previous.next = node.next
node.next.previous = node.previous
10 changes: 8 additions & 2 deletions Sprint-2/improve_with_caches/fibonacci/fibonacci.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
def fibonacci(n):
def fibonacci(n, cache=None):
if cache is None:
cache = {}
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
if n in cache:
return cache[n]
result = fibonacci(n - 1, cache) + fibonacci(n - 2, cache)
cache[n] = result
return result
27 changes: 11 additions & 16 deletions Sprint-2/improve_with_caches/making_change/making_change.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,15 @@ def ways_to_make_change(total: int) -> int:
def ways_to_make_change_helper(total: int, coins: List[int]) -> int:
"""
Helper function for ways_to_make_change to avoid exposing the coins parameter to callers.

Builds up a cache of ways[amount] = number of ways to make `amount` using the coins
considered so far, growing the cache one coin denomination at a time.
"""
if total == 0 or len(coins) == 0:
return 0

ways = 0
for coin_index in range(len(coins)):
coin = coins[coin_index]
count_of_coin = 1
while coin * count_of_coin <= total:
total_from_coins = coin * count_of_coin
if total_from_coins == total:
ways += 1
else:
intermediate = ways_to_make_change_helper(total - total_from_coins, coins=coins[coin_index+1:])
ways += intermediate
count_of_coin += 1
return ways
ways = [0] * (total + 1)
ways[0] = 1

for coin in coins:
for amount in range(coin, total + 1):
ways[amount] += ways[amount - coin]

return ways[total]