JUNE 2026 Coding interviews test recall. kodr.run tests engineering judgment
PythonIntermediate20 minGeneral

Python Data Structures Interview Questions (With Code Examples)

Why interviewers ask this

Lists, dictionaries, stacks, and queues cover the majority of real interview problems, not because interviewers care about trivia, but because choosing the right structure is most of what separates a correct-but-slow answer from an efficient one. This guide covers ten questions built around exactly that decision: which structure fits, and what it costs if you pick the wrong one.


The first four get full treatment: working code, the naive alternative, and the complexity gap between them. The remaining six are tighter: one solution and the insight it tests.

How interviewers read a data-structures answer

LayerWhat a weaker answer doesWhat a stronger answer does
Structure choiceReaches for a list by default, even when a dict or set fits betterPicks the structure based on the access pattern (lookup, order, membership) before writing code
EfficiencyUses list.pop(0) or nested loops without noticing the costKnows which list operations are O(1) versus O(n), and reaches for deque when needed
CorrectnessHandles the given example, breaks on empty input or duplicate keysNames the edge cases (empty structure, duplicate keys, single element) before coding
CommunicationPresents a finished block without explaining the choice of structureStates why this structure fits the problem before writing code

1How do you implement a stack using a list?

Python · stackimpl.py
stack = []
stack.append(1)
stack.append(2)
stack.append(3)


print(stack.pop()) # 3
print(stack.pop()) # 2
print(stack) # [1]

A Python list is already a stack. append adds to the end and pop (with no argument) removes from the end, both O(1), because both operations happen at the list's tail where no shifting is needed.


Evaluates: whether the candidate knows a plain list is sufficient for a stack, rather than reaching for a custom class or a different structure that adds complexity without benefit.

2How do you implement a queue efficiently?

Python · queueimpl.py
from collections import deque


queue = deque()
queue.append(1)
queue.append(2)
queue.append(3)


print(queue.popleft()) # 1
print(queue.popleft()) # 2
print(queue) # deque([3])

The naive version uses a plain list:


Python · queueimpl.py
# Works, but slow at scale
queue = [1, 2, 3]
queue.pop(0) # O(n): every remaining element shifts left

list.pop(0) is O(n) because every remaining element has to shift down one position. deque.popleft() is O(1), since deque is built as a doubly linked list under the hood.

Approachpop/popleft costWhy
list.pop(0)O(n)Every remaining element shifts
deque.popleft()O(1)No shifting required

Evaluates: efficiency. This is one of the most common hidden performance bugs in real code, not just interviews. A queue built on a plain list looks correct and passes small test cases, then degrades badly at scale.

3How do you find duplicates in a list efficiently?

Python · duplicates.py
def find_duplicates(nums):
    seen = set()
    duplicates = set()
    for n in nums:
        if n in seen:
            duplicates.add(n)
        else:
            seen.add(n)
    return list(duplicates)


print(find_duplicates([1, 2, 3, 2, 4, 1])) # [1, 2]

The naive version compares every pair:


Python · duplicates_slow.py
# Wrong: O(n^2)
def find_duplicates_slow(nums):
    duplicates = []
    for i in range(len(nums)):
        for j in range(i + 1, len(nums)):
            if nums[i] == nums[j] and nums[i] not in duplicates:
                duplicates.append(nums[i])
    return duplicates

Set lookups are O(1) on average, so the first version is O(n) overall. The nested-loop version is O(n²).


Evaluates: efficiency. This is close to the most commonly asked list question in any language, and the set-based answer is what most interviewers are listening for.

4How do you merge two dictionaries?

Python · merge_dict.py
defaults = {"theme": "light", "font_size": 12}
overrides = {"font_size": 14, "language": "en"}


merged = defaults | overrides
print(merged) # {'theme': 'light', 'font_size': 14, 'language': 'en'}

The | merge operator (Python 3.9+) is the modern idiom. The older approach uses update:


Python · merge_dict_old.py
merged = defaults.copy()
merged.update(overrides) # same result, mutates a copy instead

Both give the same result here, but the ordering matters: whichever dictionary comes second (overrides, or the argument to update) wins on key conflicts. Candidates who don't realise this will get the precedence backwards under pressure.


Evaluates: correctness. Knowing which dictionary wins a key conflict, and being able to state it without running the code, is the actual signal.

5How do you reverse a dictionary, swapping keys and values?

Python · reverse_dict.py
original = {"a": 1, "b": 2, "c": 3}
reversed_dict = {v: k for k, v in original.items()}
print(reversed_dict) # {1: 'a', 2: 'b', 3: 'c'}

Evaluates: whether the candidate knows this only works cleanly when the original values are unique and hashable. Duplicate values silently overwrite each other.

6How do you find the intersection of two lists?

Python · list_intersection.py
list1 = [1, 2, 3, 4]
list2 = [3, 4, 5, 6]
common = list(set(list1) & set(list2))
print(common) # [3, 4]

Evaluates: reaching for set intersection (&) instead of a nested loop, which turns an O(n×m) comparison into an O(n+m) one.

7How do you sort a list of dictionaries by a specific key?

Python · sort_dict.py
people = [{"name": "Zoe", "age": 25}, {"name": "Amit", "age": 30}, {"name": "Lin", "age": 22}]
sorted_people = sorted(people, key=lambda p: p["age"])
print(sorted_people)
# [{'name': 'Lin', 'age': 22}, {'name': 'Zoe', 'age': 25}, {'name': 'Amit', 'age': 30}]

Evaluates: comfort with key= as a sorting parameter, which comes up constantly outside interviews too, in any code that sorts structured data.

8How do you find the second largest element in a list without sorting?

Python · second_largest.py
def second_largest(nums):
    first = second = float("-inf")
    for n in nums:
        if n > first:
            first, second = n, first
        elif first > n > second:
            second = n
    return second


print(second_largest([4, 1, 7, 3, 7, 2])) # 4

Evaluates: efficiency. Sorting the whole list to find the second-largest value is O(n log n); tracking the top two values in a single pass is O(n).

9How do you group a list of tuples into a dictionary of lists?

Python · group_tuples.py
from collections import defaultdict


records = [("fruit", "apple"), ("veg", "carrot"), ("fruit", "banana")]
grouped = defaultdict(list)
for category, item in records:
    grouped[category].append(item)


print(dict(grouped)) # {'fruit': ['apple', 'banana'], 'veg': ['carrot']}

Evaluates: knowing defaultdict avoids the repetitive if key not in dict: dict[key] = [] check that a manual dictionary approach needs.

10How do you implement a queue using two stacks?

Python · queue_with_stacks.py
class QueueWithStacks:
    def __init__(self):
        self.in_stack = []
        self.out_stack = []


    def enqueue(self, item):
        self.in_stack.append(item)


    def dequeue(self):
        if not self.out_stack:
            while self.in_stack:
                self.out_stack.append(self.in_stack.pop())
        return self.out_stack.pop()


q = QueueWithStacks()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
print(q.dequeue()) # 1
print(q.dequeue()) # 2

Evaluates: a classic structure-translation question. Each individual operation can look O(n) (the transfer loop), but each element only ever moves between the two stacks once, so the cost averages out to O(1) per operation over time. Interviewers use this specifically to test amortized-complexity reasoning, not just whether the queue works.

How should you prepare for a data-structures interview?


  1. Know the real cost of common list operations. append and pop() (no argument) are O(1). pop(0), insert(0, x), and x in list are all O(n). Most performance bugs in this guide trace back to one of these.
  2. State the structure choice before coding. "I'll use a dict here because I need O(1) lookup by name" takes five seconds and tells the interviewer you're reasoning about the problem, not guessing.
  3. Practise amortized-complexity reasoning. The two-stacks queue (Q10) is the clearest example: individual operations can look expensive while the average cost stays low. Being able to explain that distinction out loud is a strong signal at any level.

Frequently asked questions


When should I use a set instead of a list in Python? Whenever the problem only needs membership checks (is this element present) rather than order or duplicates. Set lookups are O(1) average case; list lookups with in are O(n).


Is deque always better than a list for queue operations? For queue-style access (adding to one end, removing from the other), yes. For random-access by index, a list is faster, since deque indexing is O(n) while list indexing is O(1).


Why does the interview ask about merging dictionaries specifically? It is a compact way to check whether a candidate understands key-conflict precedence, which comes up constantly in real code (config merging, default overrides) and is easy to get backwards without noticing.


Do I need to memorize the time complexity of every Python built-in? Not every one, but the common list, dict, and set operations covered in this guide account for the vast majority of what actually comes up. Knowing those cold covers most interviews.



Related tutorials: Top Python Coding Interview Questions · Python String Manipulation Interview Questions · Python Pattern Programs