Skip to content
Skilled.dev

Language preparation

Python coding interview preparation

Choose lists, dictionaries, sets, deque, and heaps deliberately. Practice readable Python solutions with clear time and memory analysis.

Published · 6 min read

Python lets you express many interview algorithms with little syntax, but a short expression can still hide repeated work, a copied list, or an unhandled input case. Prepare by choosing the right collection and explaining what your implementation does with it.

This guide focuses on data structures and problem-solving habits you can reproduce in an interview. It uses standard Python syntax and library tools. Confirm the available Python version and any environment restrictions before relying on a package or an API that the editor may not provide.

Choose lists, dictionaries, and sets deliberately

Use a list for indexed sequences and stack operations at the end. Use a dictionary when a key needs an associated value, such as a count, an earlier index, or a cached answer. Use a set when membership alone is enough.

Dictionary and set keys must be hashable. A tuple can be a convenient coordinate key when its contents are hashable; a mutable list cannot serve as the same kind of key. A coordinate tuple also makes the row/column relationship explicit when tracking grid visits.

For map-based Two Sum, check for a complement before storing the current index. That order prevents a single entry from matching itself.

def two_sum(values, target):
    previous = {}
    for index, value in enumerate(values):
        complement = target - value
        if complement in previous:
            return [previous[complement], index]
        previous[value] = index
    return None

For [3, 3] and target 6, the second entry finds the first. For [3], the function returns None. Explain the expected hash-table operation model when stating O(n) time, and include the O(n) additional dictionary storage.

Use a queue with suitable end operations

A breadth-first search repeatedly removes the next item from the front. Removing index zero from a list requires moving later entries in the usual list model. collections.deque supplies end operations suited to queue behavior; see the Python collections documentation.

from collections import deque

def bfs_order(graph, start):
    queue = deque([start])
    seen = {start}
    order = []
    while queue:
        node = queue.popleft()
        order.append(node)
        for neighbor in graph.get(node, []):
            if neighbor not in seen:
                seen.add(neighbor)
                queue.append(neighbor)
    return order

Marking on insertion prevents the same neighbor from being queued by several parents. The returned order includes only nodes reachable from start. A missing key is treated here as a node with no outgoing edges; validate that contract if your exercise requires all vertices to be listed explicitly.

Practice queue state in the graph visualizer, then write the Python version without looking at the example. The representation changes across languages, while the discovery invariant remains the same.

Understand slicing and comprehensions

A list slice creates another list containing references to the selected elements. It can be a readable choice, but the copying work and storage belong in the complexity analysis. Recursive code that passes progressively smaller slices has different costs from code that passes index boundaries into the same list.

A list comprehension materializes its result. A generator expression produces values as it is consumed, which can reduce retained intermediate storage for a suitable caller. It does not remove the work required to produce those values.

Beware nested initialization that repeats a reference. [[0] * columns] * rows repeats the same inner list. Editing one apparent row affects the others. A row-by-row comprehension creates separate row lists. This matters in visited grids and dynamic-programming tables, where shared rows can produce convincing but incorrect results.

Make heap entries comparable

Python's heapq provides min-heap operations on a list. For priority-based work, make the ordering of entries explicit. A tuple such as (priority, sequence_number, item) can use a unique sequence number to break priority ties before comparison reaches an item that has no ordering relation.

For ordinary integer priorities, one way to model a maximum priority is to negate it before pushing into a min-heap. State that representation and convert back when reading the result. If a newer environment provides a direct API you want to use, verify it rather than assuming the interview editor matches your machine.

A heap is useful when repeatedly choosing the next minimum or retaining a bounded top-k set. Sorting the whole collection may be simpler when all results are needed once. Compare the number of operations and the required output before choosing the structure.

Separate a recursion limit from an algorithmic bound

A recursive tree traversal can be correct and still fail on a sufficiently deep input because the runtime call stack is finite. Practice an explicit-stack traversal for chain-shaped trees and large graphs. Raising a recursion limit is an environment change, not proof that stack usage is safe.

For recursive reasoning, define what each call returns and when it stops. Maximum depth can return zero for an empty tree and one plus the larger child depth for a node. A recursive graph search also needs visited state to avoid cycling.

When using a cache, keep its lifetime aligned with the input. A cached function that closes over mutable data can return stale answers if that data changes while keys remain the same. For a small interview solution, a cache created inside the top-level call is often easier to reason about.

Account for Python's numeric and text behavior

Python integers support values beyond a fixed machine-word range, but arithmetic on arbitrarily large integers is not constant work. Most introductory complexity explanations use a bounded numeric model. Say when that assumption matters for a problem producing very large counts.

A Python string is immutable. Building larger strings repeatedly can have costs beyond a single character assignment model. Collecting fragments and joining them is a useful construction strategy, especially when you can determine the pieces independently.

String indices operate on Unicode code points, which are not always complete user-perceived characters. If an exercise promises lowercase English letters, that constraint simplifies the character model. If it accepts general user text, clarify normalization and segmentation requirements before building frequency keys.

Test absence and valid zero results

Use membership tests when a dictionary may store a valid zero, false, empty collection, or None. The expression if memo.get(state) treats several valid results as absent. That can break a cache's performance or change a function's behavior.

Do not use a mutable default argument as a fresh per-call result list or visited set. A default object is reused across calls. Initialize per-invocation state inside the function or use a None default and create the object explicitly.

When tests allow multiple valid answers, validate the returned answer's contract. For Two Sum, check distinct indices, bounds, and the target sum. Requiring the exact same pair as a reference can reject a different valid solution.

Use a sequence that exposes different kinds of state

Start with arrays, hash tables, and strings. Then practice bracket matching with a stack, list reversal with references, and tree depth with recursive return values. Move to graph traversal after you can explain the visited set and queue separately.

For dynamic programming, implement the non-adjacent-sum recurrence with a local cache, then with a table. Compare the output on empty inputs, negative values, and a small brute-force reference.

Use the 30-day study plan to schedule those exercises and revisit them. During a timed rehearsal, explain the meaning of the collection you are using, the total work, and the memory retained by the actual Python operations you wrote.

Worked lesson walkthroughs

58 reading lessons with code examples in this language.

Concepts

Concept lesson

Arrays in Python

Learn array insertion in Python. Trace a copied result, explain shifting costs, and check empty inputs and endpoint positions without changing the original.

Concept lesson

Big O time and space complexity in Python

Analyze time and space complexity in Python with a suffix-copy example. Count nested work, returned storage, and allocation instead of counting loops alone.

Concept lesson

Hash Tables in Python

Count frequencies with a hash map in Python. Preserve input order, distinguish missing keys from zero, and explain expected lookup time and retained storage.

Concept lesson

Strings in Python

Practice string construction in Python with a repeat-text example. Explain builders, copies, Unicode representation, output size, and boundary cases.

Concept lesson

Palindromes, permutations, and anagrams in Python

Check an ASCII-normalized palindrome in Python. Trace two-pointer comparisons, case handling, ignored characters, and the limits of the text contract.

Concept lesson

Recursion in Python

Trace recursive factorial in Python from the base case through returning calls. Explain termination, stack space, integer limits, and boundary tests.

Concept lesson

Logarithmic time in Python: repeated halving and search

Understand logarithmic loops in Python by counting integer halvings. Test powers of two, explain rounding, and derive the exact step count.

Concept lesson

Binary search in Python: invariants and boundary tests

Write first-occurrence binary search in Python. Trace half-open bounds, duplicate values, missing targets, and the random-access assumption behind O(log n).

Concept lesson

Sorting in Python

Return a numerically sorted copy in Python. Preserve duplicates and the original input, choose the right library operation, and account for copied storage.

Concept lesson

Linked Lists in Python

Find a linked list's middle in Python with slow and fast runners. Define the even-length result, handle an empty list, and separate construction from traversal.

Concept lesson

Pointers in Python

Trace shared mutation and reference rebinding in Python. Follow two aliases through one object update and a new allocation, with explicit ownership notes.

Concept lesson

Queues in Python

Practice FIFO queue behavior in Python. Compare efficient front removal with a head cursor, preserve repeated values, and count fill-and-drain costs.

Concept lesson

Stacks in Python

Practice LIFO stack behavior in Python. Trace push and pop, separate empty state from zero payloads, and explain node ownership and end-operation costs.

Concept lesson

Trees in Python

Compute binary-tree edge height in Python. Read heap-indexed nullable slots, handle an empty root, and account for reachable nodes and recursion depth.

Concept lesson

Tree traversal in Python: in-order, pre-order, and post-order

Trace in-order binary-tree traversal in Python. Explain nullable child positions, visit order, recursion space, and when the output is actually sorted.

Concept lesson

Binary Search Trees in Python

Search a binary search tree in Python. Follow one ordered path, handle absent children, and relate search cost to height without assuming balance.

Concept lesson

Graphs in Python

Read graph adjacency lists in Python and compute out-degrees. Define vertex IDs, parallel edges, self-loops, representation costs, and input assumptions.

Concept lesson

Depth-First Search in Python

Implement iterative DFS in Python. Preserve listed neighbor order, skip repeated visits, trace a shared descendant, and explain pending-stack costs.

Concept lesson

Breadth-First Search in Python

Implement BFS in Python with FIFO order and enqueue-time discovery. Trace graph layers, cycles, shared descendants, and visited-storage costs.

Concept lesson

Subproblems and Overlapping Subproblems in Python

Count naive Fibonacci calls in Python. Trace overlapping states, include base-case work, and identify the cache key before applying memoization.

Concept lesson

Memoization in Python

Memoize Fibonacci in Python and count distinct computed states. Preserve cached zero, define cache lifetime, and explain remaining recursion depth.

Concept lesson

Bottom-Up vs. Top-Down in Python

Build a bottom-up Fibonacci table in Python. Follow dependency order, test zero input, and compare full-table storage with rolling-state alternatives.

Concept lesson

Dynamic Programming in Python

Solve minimum coin count in Python. Trace the recurrence, represent unreachable states, test a greedy counterexample, and explain amount-based table costs.

Data structure implementations

Implementation tutorial

Implement a dynamic array in Python

Implement a map-backed teaching array in Python. Trace push, pop, insertion, and removal, including logical length, missing indices, and obsolete entries.

Implementation tutorial

Implement a hash table in Python: collisions, updates, and removal

Build a hash table in Python with separate chaining. Follow colliding keys through lookup, update, and deletion, and explain the limits of a fixed teaching hash.

Implementation tutorial

Implement a linked list in Python: head, tail, and node identity

Build a doubly linked list in Python. Trace prepend and append, verify reciprocal links and both endpoints, and explain ownership and retention.

Implementation tutorial

Implement a queue in Python: FIFO links and empty transitions

Implement a linked queue in Python. Trace FIFO order, the last removal, and refilling an empty queue while preserving first and last endpoint invariants.

Implementation tutorial

Implement a stack in Python: push, pop, and ownership

Implement a linked stack in Python. Follow the top link through push, peek, and pop, including empty state, returned nodes, and retained chains.

Implementation tutorial

Implement a binary search tree in Python: insert and search

Build a binary search tree in Python. Trace insertion and search, preserve duplicate keys on the right, and test skewed shapes without assuming balancing.

Implementation tutorial

Implement a graph in Python: adjacency lists and edge removal

Build an adjacency-list graph in Python. Trace directed and undirected edges, self-loops, parallel entries, and removal of incoming and outgoing edges.

Practice problems

Practice walkthrough

Two Sum in Python: all index pairs with a dictionary

Use enumerate and a dictionary to return every matching pair of indexes. Follow the invariant, distinguish missing keys from index zero, and test the unique-integer contract.

Practice walkthrough

Valid brackets in Python: matching delimiters with a list stack

Write a Python bracket validator using append, pop, and a closer-to-opener dictionary. Follow a nesting trace and test text, missing openers, and crossing brackets.

Practice walkthrough

Product of array except self in Python: two passes, no division

Solve Product Array using Python lists and running prefix and suffix products. Explain the invariant, zeros, negative numbers, and the cost of large integer arithmetic.

Practice walkthrough

Counting sort for ages in Python: worked solution

Sort bounded ages with a frequency table, preserving duplicate values. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Best stock profit in Python: one transaction

Find the best single chronological stock trade with one scan. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Most frequent email in Python: hash-map solution

Count email occurrences and return the unique most frequent address. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

First Bad Version in Python: boundary binary search

Find the first true value in a monotone version predicate. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Reverse a linked list in Python: pointer walkthrough

Reverse a linked list by moving nodes from an untouched suffix to a reversed prefix. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Remove kth node from end in Python: zero-based deletion

Delete the zero-based kth node from the end of a linked list. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Merge sorted linked lists in Python: Combine Blockchains

Merge two sorted linked chains while preserving duplicate timestamps. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Count Islands in Python: DFS flood-fill walkthrough

Count side-connected land components with a destructive flood fill of the working grid. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Validate a binary search tree in Python: inherited bounds

Validate every binary-tree node against bounds inherited from all its ancestors. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Binary tree maximum depth in Python: count edges

Measure a binary tree’s longest root-to-leaf path in edges. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Lowest common ancestor in Python: return the original node

Find the lowest common ancestor in a general binary tree and return the original node. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Generate string permutations in Python: positional results

Generate positional string permutations, retaining repeated results for repeated characters. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Group palindrome permutations in Python: counts and keys

Filter palindrome permutations, then group retained strings by character multiset. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Merge intervals in Python: Interview Scheduler

Merge sorted interval coverage with an explicit touching-endpoint rule. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Min-max stack in Python: constant-time extrema

Support stack extrema through histories that retain duplicate record values. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

BFS shortest path in Python: Pilot a Rocket

Find an unweighted shortest route and reconstruct it from predecessor links. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Maze reachability in Python: memoized down/right search

Memoize directed maze reachability while keeping unknown distinct from false. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Search a rotated sorted array in Python: Shifted Search

Search a distinct rotated sorted array by identifying an ordered half. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Search a sorted matrix in Python: staircase walkthrough

Eliminate a row or column at each step in a row-and-column sorted matrix. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

House Robber in Python: maximum nonadjacent sum

Maximize a nonadjacent selection with two rolling prefix optima. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Sum decimal digits in Python: recursive solution

Sum decimal digits through a decreasing recursive integer decomposition. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Count 1, 2, or 3-step paths in Python: Three Moves

Count ordered one-, two-, and three-step routes with rolling dynamic programming. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Coin change in Python: minimum-coin dynamic programming

Minimize unlimited coin usage with a bottom-up amount table. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Validate Sudoku in Python: rows, columns, and subsquares

Validate filled Sudoku entries against rows, columns, and subsquares. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Spiral matrix traversal in Python: Walk a Matrix

Traverse a rectangle in clockwise spiral order using shrinking boundaries. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.