1. Problems 21–30
Same format as the previous modules: expand a problem to see the approach and both commented solutions.
P21
Find the Max and Min in a List
In [4, 2, 9, 1, 7], the max is 9 and the min is 1.
Find the Max and Min in a List
In [4, 2, 9, 1, 7], the max is 9 and the min is 1.
Approach: seed both "max so far" and "min so far" with the first element, then update either one whenever a new element beats the current record.
JavaScript
function findMaxMin(arr) {
let max = arr[0];
let min = arr[0];
for (const num of arr) {
if (num > max) max = num;
if (num < min) min = num;
}
return { max, min };
}
console.log(findMaxMin([4, 2, 9, 1, 7])); // { max: 9, min: 1 }
Python
def find_max_min(nums: list) -> dict:
maximum = nums[0]
minimum = nums[0]
for num in nums:
if num > maximum:
maximum = num
if num < minimum:
minimum = num
return {"max": maximum, "min": minimum}
print(find_max_min([4, 2, 9, 1, 7])) # {'max': 9, 'min': 1}
P22
Sum and Average of a List
[10, 20, 30] sums to 60, averaging 20.
Sum and Average of a List
[10, 20, 30] sums to 60, averaging 20.
Approach: accumulate a running total in one pass, then divide by how many elements there are once the loop finishes.
JavaScript
function sumAndAverage(arr) {
let sum = 0;
for (const num of arr) {
sum += num;
}
const average = sum / arr.length;
return { sum, average };
}
console.log(sumAndAverage([10, 20, 30])); // { sum: 60, average: 20 }
Python
def sum_and_average(nums: list) -> dict:
total = 0
for num in nums:
total += num
average = total / len(nums)
return {"sum": total, "average": average}
print(sum_and_average([10, 20, 30])) # {'sum': 60, 'average': 20.0}
P23
Remove Duplicates, Preserving Order
[1, 2, 2, 3, 1, 4] becomes [1, 2, 3, 4].
Remove Duplicates, Preserving Order
[1, 2, 2, 3, 1, 4] becomes [1, 2, 3, 4].
Approach: keep a set of items already seen. Walk the list once; only copy an item into the result the first time it's seen, and remember it so later repeats get skipped.
JavaScript
function removeDuplicates(arr) {
const seen = new Set();
const result = [];
for (const item of arr) {
if (!seen.has(item)) {
seen.add(item); // remember it so later duplicates get skipped
result.push(item);
}
}
return result;
}
console.log(removeDuplicates([1, 2, 2, 3, 1, 4])); // [1, 2, 3, 4]
Python
def remove_duplicates(items: list) -> list:
seen = set()
result = []
for item in items:
if item not in seen:
seen.add(item) # remember it so later duplicates get skipped
result.append(item)
return result
print(remove_duplicates([1, 2, 2, 3, 1, 4])) # [1, 2, 3, 4]
P24
Find the Second Largest Number
In [5, 1, 9, 9, 3], the second largest is 5 (duplicate 9s don't count twice).
Find the Second Largest Number
In [5, 1, 9, 9, 3], the second largest is 5 (duplicate 9s don't count twice).
Approach: track both "largest so far" and "second so far" in one pass. When a new value beats the largest, the old largest slides down into second place; when a value beats only second (and isn't equal to largest), it replaces second.
JavaScript
function secondLargest(arr) {
let largest = -Infinity;
let second = -Infinity;
for (const num of arr) {
if (num > largest) {
second = largest; // old largest becomes the new second
largest = num;
} else if (num > second && num < largest) {
second = num;
}
}
return second;
}
console.log(secondLargest([5, 1, 9, 9, 3])); // 5
Python
def second_largest(nums: list) -> float:
largest = float("-inf")
second = float("-inf")
for num in nums:
if num > largest:
second = largest # old largest becomes the new second
largest = num
elif second < num < largest:
second = num
return second
print(second_largest([5, 1, 9, 9, 3])) # 5
P25
Reverse a List In Place
[1, 2, 3, 4, 5] becomes [5, 4, 3, 2, 1], with no second list allocated.
Reverse a List In Place
[1, 2, 3, 4, 5] becomes [5, 4, 3, 2, 1], with no second list allocated.
Approach: the two-pointer swap from Module 1's palindrome check, applied to elements instead of characters — swap the ends, then move both pointers inward until they meet.
JavaScript
function reverseInPlace(arr) {
let left = 0;
let right = arr.length - 1;
while (left < right) {
[arr[left], arr[right]] = [arr[right], arr[left]]; // swap the two ends
left++;
right--;
}
return arr;
}
console.log(reverseInPlace([1, 2, 3, 4, 5])); // [5, 4, 3, 2, 1]
Python
def reverse_in_place(items: list) -> list:
left, right = 0, len(items) - 1
while left < right:
items[left], items[right] = items[right], items[left] # swap the two ends
left += 1
right -= 1
return items
print(reverse_in_place([1, 2, 3, 4, 5])) # [5, 4, 3, 2, 1]
P26
Element Frequency Count in a List
["a","b","a","c","b","a"] → a: 3, b: 2, c: 1.
Element Frequency Count in a List
["a","b","a","c","b","a"] → a: 3, b: 2, c: 1.
Approach: the exact same frequency-map pattern from Module 1's character count, just applied to arbitrary list elements instead of characters.
JavaScript
function elementFrequency(arr) {
const freq = {};
for (const item of arr) {
freq[item] = (freq[item] || 0) + 1;
}
return freq;
}
console.log(elementFrequency(["a", "b", "a", "c", "b", "a"]));
// { a: 3, b: 2, c: 1 }
Python
def element_frequency(items: list) -> dict:
freq = {}
for item in items:
freq[item] = freq.get(item, 0) + 1
return freq
print(element_frequency(["a", "b", "a", "c", "b", "a"]))
# {'a': 3, 'b': 2, 'c': 1}
P27
Check if a List Is Sorted
[1, 2, 2, 5, 9] is sorted; [1, 5, 2] isn't.
Check if a List Is Sorted
[1, 2, 2, 5, 9] is sorted; [1, 5, 2] isn't.
Approach: a list is sorted (ascending) exactly when every element is greater than or equal to the one before it — so a single pass comparing neighbors is enough to disprove it.
JavaScript
function isSorted(arr) {
for (let i = 1; i < arr.length; i++) {
if (arr[i] < arr[i - 1]) return false; // smaller than its predecessor
}
return true;
}
console.log(isSorted([1, 2, 2, 5, 9])); // true
console.log(isSorted([1, 5, 2])); // false
Python
def is_sorted(items: list) -> bool:
for i in range(1, len(items)):
if items[i] < items[i - 1]: # smaller than its predecessor
return False
return True
print(is_sorted([1, 2, 2, 5, 9])) # True
print(is_sorted([1, 5, 2])) # False
P28
Merge Two Sorted Lists
[1,3,5] + [2,4,6] → [1,2,3,4,5,6], without a re-sort.
Merge Two Sorted Lists
[1,3,5] + [2,4,6] → [1,2,3,4,5,6], without a re-sort.
Approach: two pointers, one per list. Always take the smaller of the two "current" values, advance that pointer, and repeat — then append whatever's left in the list that still has elements. This is the merge step from merge sort.
JavaScript
function mergeSorted(a, b) {
const merged = [];
let i = 0, j = 0;
while (i < a.length && j < b.length) {
if (a[i] <= b[j]) {
merged.push(a[i]); i++;
} else {
merged.push(b[j]); j++;
}
}
// one list may still have leftovers once the other is exhausted
while (i < a.length) merged.push(a[i++]);
while (j < b.length) merged.push(b[j++]);
return merged;
}
console.log(mergeSorted([1, 3, 5], [2, 4, 6])); // [1, 2, 3, 4, 5, 6]
Python
def merge_sorted(a: list, b: list) -> list:
merged = []
i = j = 0
while i < len(a) and j < len(b):
if a[i] <= b[j]:
merged.append(a[i]); i += 1
else:
merged.append(b[j]); j += 1
# one list may still have leftovers once the other is exhausted
merged.extend(a[i:])
merged.extend(b[j:])
return merged
print(merge_sorted([1, 3, 5], [2, 4, 6])) # [1, 2, 3, 4, 5, 6]
P29
Intersection of Two Lists
[1,2,2,3] ∩ [2,3,4] → [2, 3].
Intersection of Two Lists
[1,2,2,3] ∩ [2,3,4] → [2, 3].
Approach: turn the second list into a set for fast membership checks, then walk the first list keeping only items that are in that set and haven't already been added to the result.
JavaScript
function intersection(a, b) {
const setB = new Set(b);
const result = [];
for (const item of a) {
if (setB.has(item) && !result.includes(item)) {
result.push(item); // keep it once, even if it repeats in `a`
}
}
return result;
}
console.log(intersection([1, 2, 2, 3], [2, 3, 4])); // [2, 3]
Python
def intersection(a: list, b: list) -> list:
set_b = set(b)
result = []
for item in a:
if item in set_b and item not in result:
result.append(item) # keep it once, even if it repeats in `a`
return result
print(intersection([1, 2, 2, 3], [2, 3, 4])) # [2, 3]
P30
Rotate a List Left by k Positions
[1,2,3,4,5] rotated left by 2 → [3,4,5,1,2].
Rotate a List Left by k Positions
[1,2,3,4,5] rotated left by 2 → [3,4,5,1,2].
Approach: cut the list into two pieces at index k, then swap their order: everything from k onward, followed by everything before k. Reducing k modulo the list length handles rotations larger than the list itself.
JavaScript
function rotateLeft(arr, k) {
const n = arr.length;
k = k % n; // rotating by n (or a multiple of n) is the same as not rotating
return arr.slice(k).concat(arr.slice(0, k));
}
console.log(rotateLeft([1, 2, 3, 4, 5], 2)); // [3, 4, 5, 1, 2]
Python
def rotate_left(items: list, k: int) -> list:
n = len(items)
k = k % n # rotating by n (or a multiple of n) is the same as not rotating
return items[k:] + items[:k]
print(rotate_left([1, 2, 3, 4, 5], 2)) # [3, 4, 5, 1, 2]
2. Key Takeaways
- A single forward pass with one or two "running record" variables (max, min, second-largest, a running sum) solves most single-list problems without ever needing a second array.
- Sets turn "have I seen this?" and "is this in the other list?" from an O(n) scan into an O(1) lookup — that's what makes de-duplication and intersection fast.
- Two-pointer techniques (reverse in place, merge two sorted lists) show up constantly once you're comfortable holding two indices in your head at once.