How do you reverse a singly linked list?
Walk with
prevandcurr: savecurr.next, pointcurr.nextatprev, then move both forward. WhencurrisNone,previs the new head. O(n) time, O(1) space.Pythonclass ListNode: def __init__(self, val, next=None): self.val = val self.next = next def build(values): head = None for v in reversed(values): head = ListNode(v, head) return head def to_list(head): out = [] while head: out.append(head.val) head = head.next return out def reverse(head): prev, curr = None, head while curr: nxt = curr.next curr.next = prev prev, curr = curr, nxt return prev print(to_list(reverse(build([1, 2, 3, 4, 5]))))The recursive version is shorter but uses O(n) stack, which matters on long lists.
Practice it: Reverse Linked List
How do you detect a cycle in a linked list?
Use Floyd's tortoise and hare:
slowmoves one node,fasttwo. With a cycle they meet; iffastreachesNone, there is none. O(n) time, O(1) space.Pythonclass ListNode: def __init__(self, val): self.val = val self.next = None def has_cycle(head): slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next if slow is fast: return True return False nodes = [ListNode(i) for i in range(5)] for a, b in zip(nodes, nodes[1:]): a.next = b print(has_cycle(nodes[0])) nodes[4].next = nodes[2] # the tail points back into the list print(has_cycle(nodes[0]))Follow-up: where does the cycle start? After they meet, reset one pointer to the head and step both by one; they meet at the cycle's first node.
Practice it: Linked List Cycle, Find the Duplicate Number
How do you find the middle of a linked list in one pass?
Move
slowone step andfasttwo; whenfastreaches the end,slowis at the middle. For an even length it returns the second middle node.Pythonclass ListNode: def __init__(self, val, next=None): self.val = val self.next = next def middle(head): slow = fast = head while fast and fast.next: slow = slow.next fast = fast.next.next return slow.val five = ListNode(1, ListNode(2, ListNode(3, ListNode(4, ListNode(5))))) six = ListNode(1, ListNode(2, ListNode(3, ListNode(4, ListNode(5, ListNode(6)))))) print(middle(five), middle(six))It is the first step of merge sort on a linked list.
Practice it: Middle of the Linked List, Remove Nth Node From End of List
What is the difference between singly, doubly and circular linked lists?
A singly linked node points to the next node; a doubly linked node also points to the previous one; in a circular list the last node points back to the first. Only a doubly linked list can delete a node you hold in O(1), which is why LRU caches use one. A circular traversal must stop when it returns to the start.
Practice it: Reverse Linked List
How do you check if a string of brackets is balanced?
Push every opening bracket; each closing bracket must match the popped top; at the end the stack must be empty. O(n) time and space.
Pythondef is_valid(s): pairs = {")": "(", "]": "[", "}": "{"} stack = [] for ch in s: if ch in pairs: if not stack or stack.pop() != pairs[ch]: return False else: stack.append(ch) return not stack for s in ["()[]{}", "([)]", "{[]}", "(("]: print(s, is_valid(s))Test a closer on an empty stack, a mismatch (
([)]) and leftover openers ((().Practice it: Valid Parentheses, Generate Parentheses
What is left in a Python list stack after three pushes, a pop and a push?
Pythonstack = [] for x in [1, 2, 3]: stack.append(x) stack.pop() stack.append(4) print(stack)After the loop the stack is
[1, 2, 3].pop()removes the top, 3, then 4 goes on top:[1, 2, 4]. Picking[2, 3, 4]means you treated it as a queue.Practice it: Baseball Game
What does a Python
dequehold afterappend,popleftandappendleft?Pythonfrom collections import deque q = deque([1, 2, 3]) q.append(4) q.popleft() q.appendleft(0) print(list(q))append(4)gives[1, 2, 3, 4],popleft()removes 1, andappendleft(0)puts 0 in front:[0, 2, 3, 4]. Both ends are O(1), which is why BFS usesdeque.popleft()and notlist.pop(0), an O(n) shift.How do you implement a queue using two stacks?
Push onto an
inboxstack and pop from anoutboxstack; when the outbox is empty, move everything from the inbox onto it, reversing the order. Each item moves at most twice, so operations are amortized O(1).Pythonclass Queue: def __init__(self): self.inbox, self.outbox = [], [] def push(self, x): self.inbox.append(x) def pop(self): if not self.outbox: while self.inbox: self.outbox.append(self.inbox.pop()) return self.outbox.pop() q = Queue() for x in [1, 2, 3]: q.push(x) print(q.pop()) q.push(4) print(q.pop(), q.pop(), q.pop())Moving items back to the inbox after every pop is the common wrong version, O(n) per operation.
Practice it: Implement Queue Using Stacks
How do you design a stack that returns its minimum in O(1)?
Push each value together with the minimum of it and everything below it. The top then always holds the current minimum, and popping restores the previous one. Every operation is O(1).
Pythonclass MinStack: def __init__(self): self.items = [] # (value, minimum so far) def push(self, x): low = min(x, self.items[-1][1]) if self.items else x self.items.append((x, low)) def pop(self): return self.items.pop()[0] def get_min(self): return self.items[-1][1] s = MinStack() for x in [5, 3, 7, 1]: s.push(x) print(s.get_min()) s.pop() print(s.get_min()) s.pop() s.pop() print(s.get_min())It prints 1, then 3, then 5. A leaner version keeps a second stack pushed only when the value is
<=its top; with<, popping one of two equal minimums would lose the other.Practice it: Min Stack
What is a monotonic stack, and how does it find the next greater element?
A monotonic stack keeps its elements in order by popping anything that would break it before pushing. For next greater element, keep indexes still waiting; a bigger value answers every smaller one it pops. Each index is pushed and popped once: O(n).
Pythondef next_greater(nums): result = [-1] * len(nums) stack = [] # indexes whose values never increase for i, x in enumerate(nums): while stack and nums[stack[-1]] < x: result[stack.pop()] = x stack.append(i) return result print(next_greater([2, 1, 2, 4, 3])) print(next_greater([73, 74, 75, 71, 69, 72, 76, 73]))The same loop solves daily temperatures and the largest rectangle in a histogram.
Practice it: Next Greater Element I, Daily Temperatures, Largest Rectangle in Histogram
What is a heap, and how do you use it as a priority queue?
A binary heap is a complete binary tree in an array where every parent is at most its children (a min heap). The smallest item sits at index 0, push and pop are O(log n), and building one is O(n). Python's
heapqis a min heap; negate values for a max heap.Pythonimport heapq tasks = [(3, "write tests"), (1, "fix prod bug"), (2, "review PR")] heapq.heapify(tasks) while tasks: priority, name = heapq.heappop(tasks) print(priority, name) # max heap trick: push negated values nums = [5, 1, 8, 3] max_heap = [-x for x in nums] heapq.heapify(max_heap) print(-max_heap[0])Top k uses a min heap of size k, O(n log k). Watch heap insert and remove.
Practice it: Kth Largest Element in an Array, Last Stone Weight, Merge k Sorted Lists
What does
h[0]show before and after aheappopin Python?Pythonimport heapq h = [] for x in [5, 1, 8, 3]: heapq.heappush(h, x) print(h[0], heapq.heappop(h), h[0])h[0]is always the smallest item, so the first value is 1. Arguments are evaluated left to right:heappopreturns 1, thenh[0]is the new smallest, 3. Only index 0 is guaranteed; the rest of the list is not sorted.How would you design an LRU cache with O(1) get and put?
Combine a hash map with a doubly linked list. The map finds a key's node in O(1); the list keeps nodes in order of use, so moving one to the front and evicting the tail are O(1).
OrderedDictis that pair:Pythonfrom collections import OrderedDict class LRUCache: def __init__(self, capacity): self.capacity = capacity self.data = OrderedDict() def get(self, key): if key not in self.data: return -1 self.data.move_to_end(key) return self.data[key] def put(self, key, value): self.data[key] = value self.data.move_to_end(key) if len(self.data) > self.capacity: self.data.popitem(last=False) # least recently used cache = LRUCache(2) cache.put("a", 1) cache.put("b", 2) cache.get("a") cache.put("c", 3) # evicts "b" print(cache.get("b"), cache.get("a"), cache.get("c"))Expect to write the list by hand; sentinel head and tail nodes remove the
Nonechecks. See a doubly linked list.
Linked List, Stack and Queue Interview Questions
Linked list pointer work, stacks and queues, heaps as priority queues, and the LRU cache.