Python Coding Exercise Algorithms

These ten short exercises are the warm-up problems that appear in screening interviews and introductory courses. Each one is solvable in a few lines of Python, but each also illustrates a reusable technique: slicing, a hash map for constant-time lookup, two pointers over sorted input, counting with a dictionary, or set algebra. This page gives a working solution for each, then explains the complexity, traces two of the more instructive ones step by step, and lists the edge cases interviewers usually probe.

1. Techniques behind the exercises

Most of the problems reduce to one of four patterns. Built-ins and slicing (reverse, largest, palindrome) lean on operations implemented in C, which are both shorter and faster than hand-written loops. Hash maps (two sum, first non-repeated character, vowels) trade O(n) memory for O(1) membership checks, turning nested loops into single passes. Two pointers (merge sorted lists) exploit existing order so each element is examined once. Sets (duplicates, intersection) express membership problems directly, at the cost of losing order. Recognizing which pattern applies is more valuable than memorizing the individual answers.

2. Complexity at a glance

#ExerciseTimeExtra spaceTechnique
1Reverse a stringO(n)O(n)Slicing
2Largest numberO(n)O(1)Built-in max
3Number palindromeO(d)O(d)String compare (d digits)
4Fibonacci listO(n)O(n)Iteration
5Two sumO(n)O(n)Hash map
6Remove duplicatesO(n)O(n)Set
7Count vowelsO(n)O(1)Generator + membership
8Merge sorted listsO(n + m)O(n + m)Two pointers
9First non-repeated charO(n)O(k) distinct charsCounting dict
10List intersectionO(n + m)O(n + m)Set intersection

The naive alternatives are worth knowing because interviewers often ask for the improvement: two sum with nested loops is O(n²); merging by concatenating and calling sorted() is O((n + m) log(n + m)); intersection with [x for x in a if x in b] on lists is O(n·m).

3. The ten solutions

1. Reverse a String

def reverse_string(s: str) -> str:
    return s[::-1]

# Example usage
print(reverse_string("hello"))  # Output: "olleh"
    

2. Find the Largest Number in a List

def find_largest(nums: list) -> int:
    return max(nums)

# Example usage
print(find_largest([1, 2, 3, 4, 5]))  # Output: 5
    

3. Check if a Number is a Palindrome

def is_palindrome(n: int) -> bool:
    return str(n) == str(n)[::-1]

# Example usage
print(is_palindrome(121))  # Output: True
print(is_palindrome(123))  # Output: False
    

4. Fibonacci Sequence

def fibonacci(n: int) -> list:
    fib_seq = [0, 1]
    for i in range(2, n):
        fib_seq.append(fib_seq[-1] + fib_seq[-2])
    return fib_seq[:n]

# Example usage
print(fibonacci(5))  # Output: [0, 1, 1, 2, 3]
    

5. Two Sum Problem

def two_sum(nums: list, target: int) -> list:
    num_map = {}
    for i, num in enumerate(nums):
        complement = target - num
        if complement in num_map:
            return [num_map[complement], i]
        num_map[num] = i

# Example usage
print(two_sum([2, 7, 11, 15], 9))  # Output: [0, 1]
    

6. Remove Duplicates from a List

def remove_duplicates(nums: list) -> list:
    return list(set(nums))

# Example usage
print(remove_duplicates([1, 2, 2, 3, 4, 4, 5]))  # Output: [1, 2, 3, 4, 5]
    

7. Count the Number of Vowels in a String

def count_vowels(s: str) -> int:
    vowels = 'aeiouAEIOU'
    return sum(1 for char in s if char in vowels)

# Example usage
print(count_vowels("hello world"))  # Output: 3
    

8. Merge Two Sorted Lists

def merge_sorted_lists(list1: list, list2: list) -> list:
    merged = []
    i = j = 0
    while i < len(list1) and j < len(list2):
        if list1[i] < list2[j]:
            merged.append(list1[i])
            i += 1
        else:
            merged.append(list2[j])
            j += 1
    merged.extend(list1[i:])
    merged.extend(list2[j:])
    return merged

# Example usage
print(merge_sorted_lists([1, 3, 5], [2, 4, 6]))  # Output: [1, 2, 3, 4, 5, 6]
    

9. Find the First Non-Repeated Character in a String

def first_non_repeated_char(s: str) -> str:
    counts = {}
    for char in s:
        counts[char] = counts.get(char, 0) + 1
    for char in s:
        if counts[char] == 1:
            return char
    return None

# Example usage
print(first_non_repeated_char("swiss"))  # Output: "w"
    

10. Find the Intersection of Two Lists

def list_intersection(list1: list, list2: list) -> list:
    return list(set(list1) & set(list2))

# Example usage
print(list_intersection([1, 2, 3, 4], [3, 4, 5, 6]))  # Output: [3, 4]
    

4. Trace: Two Sum with a hash map

two_sum([2, 7, 11, 15], target=9) nums 2 7 11 15 i=0: need 7, map {} miss; store 2:0 2 7 11 15 i=1: need 2, map {2:0} hit 2 7 11 15 return [0, 1] 0 1 The complement is checked before the current value is stored, so an element never pairs with itself.

For each element the function asks one question: "have I already seen the number that would complete the sum?" The dictionary answers in O(1) on average, so the whole scan is a single pass. Storing after checking matters: with nums = [3, 3] and target 6, the first 3 is stored, and the second finds it, returning [0, 1]; with target 6 and nums = [3] the single 3 never matches itself. If no pair exists, the function as written falls off the end and returns None, which callers should handle.

5. Trace: merging two sorted lists

merge_sorted_lists([1, 3, 5], [2, 4, 6]) compare 1 vs 2, take 1 (i=1) 1 compare 3 vs 2, take 2 (j=1) 1 2 compare 3 vs 4, take 3 (i=2) 1 2 3 compare 5 vs 4, take 4 (j=2) 1 2 3 4 compare 5 vs 6, take 5 (i=3) 1 2 3 4 5 list1 exhausted: extend with [6] 1 2 3 4 5 6 Each comparison emits exactly one element, so the loop runs at most n + m - 1 times.

Two indexes walk the lists in parallel, and the smaller of the two current heads is appended each time. When one list runs out, the remainder of the other is already sorted and is appended in one extend. Using < rather than <= means equal values come from list2 first; switching to <= makes the merge stable with respect to list1, which is the variant merge sort needs. The standard library offers the same operation lazily as heapq.merge(list1, list2).

6. Edge cases and pitfalls