Python Coding Interview Patterns: Lists, Strings & Hash Maps

Part 1 of the Python Interview Prep track. Last updated: October 2026.

Most Python coding interview questions are not about memorizing hundreds of problems — they are about recognizing six patterns that repeat everywhere: hash map lookups, two pointers, sliding windows, and frequency counting. Master these patterns and you can walk into interviews solving problems you have never seen before. Every example below is runnable Python 3.

1. Two Sum — the hash map single-pass pattern

Problem: given a list of numbers and a target, return the indices of the two numbers that add up to the target.

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

print(two_sum([2, 7, 11, 15], 9))   # [0, 1]
print(two_sum([3, 2, 4], 6))        # [1, 2]
print(two_sum([3, 3], 6))           # [0, 1]

Why it works: instead of checking every pair (O(n2)), we store each number's index in a dictionary and look up its complement in O(1). We check before inserting, so a number never pairs with itself. Time O(n), Space O(n).

Common follow-up: "find all pairs, not just one." Track pairs instead of returning early:

def two_sum_all(nums, target):
    seen = set()
    pairs = []
    for num in nums:
        if target - num in seen:
            pairs.append((target - num, num))
        seen.add(num)
    return pairs

print(two_sum_all([1, 2, 3, 4, 3], 6))   # [(2, 4), (3, 3)]

Say this in the interview: "The naive nested loop is O(n2); the hash map trades O(n) space for O(n) time."

2. Valid Anagram — frequency counting

Problem: decide if two strings are anagrams (same letters, same counts).

# Approach A: sort both strings (easy, but slower)
def is_anagram_sorted(s, t):
    return sorted(s) == sorted(t)

print(is_anagram_sorted("anagram", "nagaram"))  # True
print(is_anagram_sorted("rat", "car"))          # False

Sorting costs O(n log n). Interviewers expect you to name this approach and then improve it:

# Approach B: count frequencies (what interviewers want)
from collections import Counter

def is_anagram(s, t):
    return Counter(s) == Counter(t)

print(is_anagram("anagram", "nagaram"))  # True
print(is_anagram("rat", "car"))          # False

Why it works: anagrams are exactly strings with identical character counts, and Counter builds that count table in a single pass. Time O(n), Space O(n) — and for a fixed alphabet you can shrink space to O(1) with a 26-element frequency array. Mention that in the interview; it shows depth.

3. Longest Substring Without Repeating Characters — sliding window

Problem: find the length of the longest substring with no repeated characters.

def length_of_longest_substring(s):
    seen = set()
    left = 0
    longest = 0
    for right in range(len(s)):
        while s[right] in seen:   # shrink the window until it's unique
            seen.remove(s[left])
            left += 1
        seen.add(s[right])
        longest = max(longest, right - left + 1)
    return longest

print(length_of_longest_substring("abcabcbb"))  # 3  ("abc")
print(length_of_longest_substring("bbbbb"))     # 1  ("b")
print(length_of_longest_substring("pwwkew"))    # 3  ("wke")

Why it works: left and right define a window that always contains unique characters. When a duplicate arrives at right, we move left forward until the window is valid again. Each character enters and leaves the set once, so the whole string is processed in a single linear pass. Time O(n), Space O(n) (the set holds at most the alphabet size).

Walk through "pwwkew" out loud if asked: window grows p → pw, then w repeats so left moves past the first w, window becomes wk → wke, and the max length 3 sticks.

4. Two Pointers on a sorted array

Problem: Two Sum again, but the array is sorted and indices are 1-based.

def two_sum_sorted(nums, target):
    left, right = 0, len(nums) - 1
    while left < right:
        total = nums[left] + nums[right]
        if total == target:
            return [left + 1, right + 1]   # 1-indexed per the problem
        if total < target:
            left += 1    # sum too small: need a bigger left value
        else:
            right -= 1   # sum too big: need a smaller right value
    return []

print(two_sum_sorted([2, 7, 11, 15], 9))   # [1, 2]
print(two_sum_sorted([2, 3, 4], 6))        # [1, 3]

Why it works: in a sorted array, the sum's direction tells you which pointer to move — no hash map needed, so space drops to O(1). Time O(n), Space O(1).

Follow-up you must be ready for: "what if the input is unsorted?" Answer with the trade-off out loud: sorting first costs O(n log n) and loses original indices (unless you sort value-index pairs), while the hash map version stays O(n) time and keeps indices — so unsorted input means the hash map approach wins.

4b. Remove Duplicates from Sorted Array — in-place two pointers

Problem: remove duplicates from a sorted list in place, using O(1) extra space, and return the new length.

def remove_duplicates(nums):
    if not nums:
        return 0
    write = 1
    for read in range(1, len(nums)):
        if nums[read] != nums[write - 1]:
            nums[write] = nums[read]
            write += 1
    return write

nums = [1, 1, 2]
k = remove_duplicates(nums)
print(k, nums[:k])   # 2 [1, 2]

Why it works: the read pointer scans every element while the write pointer marks where the next unique value goes — classic "fast/slow pointer" separation of reading and writing. Time O(n), Space O(1). This exact read/write skeleton also solves Remove Element and Move Zeroes, so learn the shape once.

5. Top K Frequent Elements — Counter + most_common

Problem: given a list, return the k most frequent elements.

from collections import Counter

def top_k_frequent(nums, k):
    counts = Counter(nums)
    return [num for num, _ in counts.most_common(k)]

print(top_k_frequent([1, 1, 1, 2, 2, 3], 2))   # [1, 2]
print(top_k_frequent([1], 1))                  # [1]

Why it works: Counter does the frequency counting in O(n), and most_common(k) handles the ranking. Time O(n), Space O(n).

Follow-up to mention: if the interviewer bans most_common, reach for a min-heap — heapq.nlargest(k, counts.items(), key=lambda x: x[1]) gives the same answer in O(n log k), which matters when k is much smaller than the number of distinct elements.

6. Group Anagrams — hash map with a signature key

Problem: group strings that are anagrams of each other.

def group_anagrams(strs):
    groups = {}
    for word in strs:
        key = tuple(sorted(word))   # the "signature" every anagram shares
        groups.setdefault(key, []).append(word)
    return list(groups.values())

print(group_anagrams(["eat", "tea", "tan", "ate", "nat", "bat"]))
# [['ate', 'eat', 'tea'], ['bat'], ['nat', 'tan']]

Why it works: every anagram of "eat" sorts to the same tuple ('a', 'e', 't'), so that tuple is a perfect dictionary key — words that share it land in the same bucket. Time O(n · k log k) where k is the longest word (the sort), Space O(n). If the alphabet is fixed, a 26-count tuple as the key drops the log factor — another nice detail to volunteer.

Key takeaways

  • Hash map (dict) → use when you need O(1) lookups of complements or seen values: Two Sum, frequency counting, anagram grouping. Time O(n), Space O(n).
  • Sliding window → use for longest/shortest subarray or substring problems with a validity condition: shrink from the left until the window is valid again. Time O(n), Space O(n) (or O(1) for a fixed alphabet).
  • Two pointers → use on sorted arrays or when you can separate reading from writing in place: sorted Two Sum, Remove Duplicates. Time O(n), Space O(1).
  • Counter + most_common → the fastest way to rank by frequency in an interview; know the heapq fallback (heapq.nlargest) for the "what if most_common didn't exist" follow-up.
  • Signature keys → when items need grouping by a canonical form, build a hashable signature (sorted tuple, count tuple) and bucket on it.
  • Always state complexity out loud and offer the trade-off: "sorted is O(n log n) with O(1) space, hash map is O(n) time with O(n) space."

Next in this series: Python Internals for Interviews: Decorators, Generators, GIL & OOP.

Comments

Popular posts from this blog

Java Banking Finance Services and Insurance (BFSI) domain interview questions

JSP Servlet Interview Questions For Freshers Series 1

Java program to check even or odd number