Interview Prep · 50 Questions

The 50 Most Common Technical Interview Questions

Every question includes the approach, a clean solution, complexity analysis, and what the interviewer is actually looking for. Grouped by topic, ordered by how often they appear at Google, Amazon, Meta, and 459+ other companies.

summarize

TL;DR

  1. 1.

    This page covers the 50 most frequently asked technical interview questions across Google, Amazon, Meta, Apple, Microsoft, and 459+ other companies.

  2. 2.

    Every question includes the approach, a Python solution, complexity analysis, and what the interviewer is actually testing.

  3. 3.

    Questions are grouped into 13 topics: arrays, two pointers, sliding window, binary search, stacks, linked lists, trees, heaps, graphs, dynamic programming, greedy, backtracking, and design.

  4. 4.

    Work topic by topic. Understand the pattern, not just the solution. Then practice under interview conditions to close the gap between solving and passing.

What is a technical interview?

A technical interview is a live evaluation where a software engineer solves coding problems while explaining their thought process to an interviewer. It is the standard hiring format at Google, Amazon, Meta, Apple, Microsoft, and most tech companies.

You typically get one or two problems in 45 to 60 minutes. The interviewer watches you code in real time, asks follow-up questions about your approach, and evaluates your problem-solving ability, communication, code quality, and understanding of time and space complexity.

The problems are drawn from well-known categories: arrays, strings, trees, graphs, dynamic programming, and more. Companies pull from a shared pool of questions, which is why the same problems keep appearing across different companies.

The most common technical interview questions

The 50 questions below are the ones that appear most often in real technical interviews. We sourced them from interview reports at 459+ companies including Google, Amazon, Meta, Apple, Microsoft, Netflix, Uber, Bloomberg, Adobe, and LinkedIn.

The breakdown: 14 Easy, 30 Medium, 6 Hard, spread across 13 core topics. Arrays and dynamic programming carry the most weight. If you can solve every problem on this page and explain your approach clearly under pressure, you are prepared for the majority of technical interviews at any company.

Each question includes the problem statement, the approach you should take, a clean Python solution, time and space complexity, and a note on what the interviewer is actually testing. Scroll down or jump to a topic below.

table_rows

Arrays & Hashing

8 questions

1

Two Sum

Easy

Given an array of integers and a target, return the indices of the two numbers that add up to the target. Each input has exactly one solution.

Approach

Use a hash map to store each number's index as you iterate. For each element, check if target minus the current number exists in the map. If it does, you've found the pair. This avoids the O(n²) brute force of checking every pair.

Python
Time: O(n)|Space: O(n)
def twoSum(nums, target):
    seen = {}
    for i, n in enumerate(nums):
        if target - n in seen:
            return [seen[target - n], i]
        seen[n] = i
tips_and_updates

What interviewers look for: Interviewers expect O(n). If you start with brute force, immediately say you can optimize with a hash map. They want to hear you identify the time-space trade-off.

Given an array where prices[i] is the price of a stock on day i, find the maximum profit from one buy and one sell. You must buy before you sell.

Approach

Track the minimum price seen so far as you iterate. At each step, calculate the profit if you sold today (current price minus minimum). Keep the maximum profit. One pass, constant space.

Python
Time: O(n)|Space: O(1)
def maxProfit(prices):
    min_price = float('inf')
    max_profit = 0
    for price in prices:
        min_price = min(min_price, price)
        max_profit = max(max_profit, price - min_price)
    return max_profit
tips_and_updates

What interviewers look for: Don't try to find the actual buy/sell days. The interviewer cares about the greedy insight: you only need to track the running minimum and the running best profit.

Given an integer array, return true if any value appears at least twice.

Approach

Add each element to a set. If the element already exists in the set, return true. If you finish iterating without a duplicate, return false. The set gives O(1) lookups.

Python
Time: O(n)|Space: O(n)
def containsDuplicate(nums):
    seen = set()
    for n in nums:
        if n in seen:
            return True
        seen.add(n)
    return False
tips_and_updates

What interviewers look for: This is a warm-up question. Solve it fast and clean. The follow-up might be: what if you can't use extra space? Then sort the array first and check adjacent elements.

Given two strings s and t, return true if t is an anagram of s (same characters, same frequency).

Approach

Count character frequencies in both strings and compare. You can use a single counter: increment for s, decrement for t, then check all values are zero.

Python
Time: O(n)|Space: O(1),bounded by alphabet size
def isAnagram(s, t):
    if len(s) != len(t):
        return False
    count = {}
    for c in s:
        count[c] = count.get(c, 0) + 1
    for c in t:
        count[c] = count.get(c, 0) - 1
        if count[c] < 0:
            return False
    return True
tips_and_updates

What interviewers look for: The follow-up is always: what if the inputs can contain Unicode? Your answer should still work since you're using a hash map, not a fixed-size array.

Given an array of strings, group the anagrams together. Two strings are anagrams if they have the same characters in the same frequency.

Approach

For each string, create a key by sorting its characters (or by counting character frequencies). Use this key in a hash map where the value is a list of strings sharing that key. Return all the lists.

Python
Time: O(n · k log k) where k is max string length|Space: O(n · k)
def groupAnagrams(strs):
    groups = {}
    for s in strs:
        key = tuple(sorted(s))
        groups.setdefault(key, []).append(s)
    return list(groups.values())
tips_and_updates

What interviewers look for: Mention the character-count key alternative for O(n · k) time. Interviewers like seeing you weigh both approaches and choose based on constraints.

Given an integer array, return an array where answer[i] equals the product of all elements except nums[i]. You cannot use division.

Approach

Build two passes: a prefix pass (left to right) storing the product of all elements before each index, and a suffix pass (right to left) multiplying the product of all elements after. The answer at each index is prefix × suffix.

Python
Time: O(n)|Space: O(1),output array doesn't count
def productExceptSelf(nums):
    n = len(nums)
    answer = [1] * n
    prefix = 1
    for i in range(n):
        answer[i] = prefix
        prefix *= nums[i]
    suffix = 1
    for i in range(n - 1, -1, -1):
        answer[i] *= suffix
        suffix *= nums[i]
    return answer
tips_and_updates

What interviewers look for: The constraint "no division" is the whole point. Immediately mention prefix/suffix products. The follow-up is doing it in O(1) extra space by reusing the output array for the prefix pass.

Given an integer array and k, return the k most frequent elements. The answer is guaranteed to be unique.

Approach

Count frequencies with a hash map, then use a bucket sort where index = frequency. Walk the buckets from highest to lowest, collecting elements until you have k. This avoids the O(n log n) of sorting.

Python
Time: O(n)|Space: O(n)
def topKFrequent(nums, k):
    count = {}
    for n in nums:
        count[n] = count.get(n, 0) + 1
    buckets = [[] for _ in range(len(nums) + 1)]
    for num, freq in count.items():
        buckets[freq].append(num)
    result = []
    for i in range(len(buckets) - 1, -1, -1):
        for num in buckets[i]:
            result.append(num)
            if len(result) == k:
                return result
tips_and_updates

What interviewers look for: Many candidates jump to a heap (O(n log k)). Mention that bucket sort gives O(n). Interviewers appreciate seeing you consider the optimal approach, even if the heap solution is acceptable.

Given an n×n 2D matrix, rotate the image 90 degrees clockwise in-place.

Approach

Transpose the matrix (swap rows and columns), then reverse each row. This gives a 90-degree clockwise rotation. The transpose swaps matrix[i][j] with matrix[j][i].

Python
Time: O(n²)|Space: O(1)
def rotate(matrix):
    n = len(matrix)
    # transpose
    for i in range(n):
        for j in range(i + 1, n):
            matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
    # reverse each row
    for row in matrix:
        row.reverse()
tips_and_updates

What interviewers look for: The transpose-then-reverse trick is clean and easy to remember. For counterclockwise, reverse then transpose. The interviewer may ask for a layer-by-layer approach (rotating four cells at a time), which is harder to implement but more educational.

compare_arrows

Two Pointers

4 questions

Given a string, determine if it is a palindrome considering only alphanumeric characters and ignoring case.

Approach

Use two pointers: one at the start, one at the end. Skip non-alphanumeric characters. Compare characters (case-insensitive) and move inward. If they ever don't match, return false.

Python
Time: O(n)|Space: O(1)
def isPalindrome(s):
    l, r = 0, len(s) - 1
    while l < r:
        while l < r and not s[l].isalnum():
            l += 1
        while l < r and not s[r].isalnum():
            r -= 1
        if s[l].lower() != s[r].lower():
            return False
        l += 1
        r -= 1
    return True
tips_and_updates

What interviewers look for: The key is doing it in-place without creating a cleaned string. Interviewers want to see you handle the edge cases (empty string, all non-alphanumeric) cleanly.

10

3Sum

Medium

Given an array, find all unique triplets that sum to zero. No duplicate triplets allowed.

Approach

Sort the array. For each element, use two pointers on the remaining subarray to find pairs that sum to the negation of the current element. Skip duplicates at every level to avoid repeated triplets.

Python
Time: O(n²)|Space: O(1),ignoring output
def threeSum(nums):
    nums.sort()
    result = []
    for i in range(len(nums) - 2):
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        l, r = i + 1, len(nums) - 1
        while l < r:
            total = nums[i] + nums[l] + nums[r]
            if total < 0:
                l += 1
            elif total > 0:
                r -= 1
            else:
                result.append([nums[i], nums[l], nums[r]])
                while l < r and nums[l] == nums[l + 1]:
                    l += 1
                l += 1
                r -= 1
    return result
tips_and_updates

What interviewers look for: The duplicate-skipping logic is where most candidates fail. Walk through an example with duplicates to show you handle it. Interviewers specifically test this.

Given n vertical lines on a chart, find two lines that together with the x-axis form a container holding the most water.

Approach

Start with pointers at both ends (widest container). Calculate the area. Move the pointer pointing to the shorter line inward, because moving the taller one can never increase the area. Repeat until pointers meet.

Python
Time: O(n)|Space: O(1)
def maxArea(height):
    l, r = 0, len(height) - 1
    best = 0
    while l < r:
        area = min(height[l], height[r]) * (r - l)
        best = max(best, area)
        if height[l] < height[r]:
            l += 1
        else:
            r -= 1
    return best
tips_and_updates

What interviewers look for: The interviewer will ask you to prove why the greedy approach works. Explain that moving the taller pointer can't help because the area is bounded by the shorter line and the width is shrinking.

Given an elevation map where bar width is 1, compute how much water can be trapped after rain.

Approach

Two pointer approach. Water at any position is min(max_left, max_right) - height[i]. Use left and right pointers, tracking max height from each side. Process the side with the smaller max, since that's the bottleneck for water level.

Python
Time: O(n)|Space: O(1)
def trap(height):
    l, r = 0, len(height) - 1
    left_max = right_max = 0
    water = 0
    while l < r:
        if height[l] < height[r]:
            left_max = max(left_max, height[l])
            water += left_max - height[l]
            l += 1
        else:
            right_max = max(right_max, height[r])
            water += right_max - height[r]
            r -= 1
    return water
tips_and_updates

What interviewers look for: Start with the prefix max arrays approach (O(n) space) for clarity, then optimize to two pointers. The insight that you can process the smaller side safely is the hard part. Draw it out.

swap_horiz

Sliding Window

2 questions

Given a string, find the length of the longest substring without repeating characters.

Approach

Maintain a sliding window with a set of characters in the current window. Expand the right pointer. When you hit a duplicate, shrink from the left until the duplicate is removed. Track the maximum window size.

Python
Time: O(n)|Space: O(min(n, alphabet))
def lengthOfLongestSubstring(s):
    chars = set()
    l = 0
    best = 0
    for r in range(len(s)):
        while s[r] in chars:
            chars.remove(s[l])
            l += 1
        chars.add(s[r])
        best = max(best, r - l + 1)
    return best
tips_and_updates

What interviewers look for: You can optimize further with a hash map storing the last index of each character, jumping the left pointer directly. Mention this as an optimization even if you code the set version.

Given strings s and t, find the minimum window in s that contains all characters of t (including duplicates).

Approach

Use two pointers and a frequency map for t. Expand right to include characters, tracking how many required characters are satisfied. Once all are satisfied, shrink from left to minimize the window. Track the best window found.

Python
Time: O(n + m)|Space: O(m),m is length of t
def minWindow(s, t):
    from collections import Counter
    need = Counter(t)
    missing = len(t)
    l = 0
    best = (0, float('inf'))
    for r, c in enumerate(s):
        if need[c] > 0:
            missing -= 1
        need[c] -= 1
        while missing == 0:
            if r - l < best[1] - best[0]:
                best = (l, r)
            need[s[l]] += 1
            if need[s[l]] > 0:
                missing += 1
            l += 1
    return "" if best[1] == float('inf') else s[best[0]:best[1]+1]
tips_and_updates

What interviewers look for: This is a classic hard sliding window. The key insight interviewers test: you need to track when all characters are 'satisfied' using a counter, not just checking the full map each time.

layers

Stack

2 questions

Given a string containing just '(', ')', '{', '}', '[' and ']', determine if the input string is valid. Every open bracket must be closed by the same type in the correct order.

Approach

Use a stack. Push opening brackets. When you hit a closing bracket, check that the stack's top is the matching opener. If it doesn't match or the stack is empty, it's invalid. At the end, the stack must be empty.

Python
Time: O(n)|Space: O(n)
def isValid(s):
    stack = []
    pairs = {')': '(', ']': '[', '}': '{'}
    for c in s:
        if c in pairs:
            if not stack or stack[-1] != pairs[c]:
                return False
            stack.pop()
        else:
            stack.append(c)
    return len(stack) == 0
tips_and_updates

What interviewers look for: This is a warm-up but interviewers watch for edge cases: empty string (valid), single bracket (invalid), nested brackets. Handle them all without special-casing.

19

Min Stack

Medium

Design a stack that supports push, pop, top, and retrieving the minimum element, all in O(1) time.

Approach

Use two stacks: a regular stack and a min stack. When you push, also push onto the min stack if the value is less than or equal to the current minimum. When you pop, also pop from the min stack if the values match. getMin returns the top of the min stack.

Python
Time: O(1) for all operations|Space: O(n)
class MinStack:
    def __init__(self):
        self.stack = []
        self.min_stack = []

    def push(self, val):
        self.stack.append(val)
        if not self.min_stack or val <= self.min_stack[-1]:
            self.min_stack.append(val)

    def pop(self):
        if self.stack[-1] == self.min_stack[-1]:
            self.min_stack.pop()
        self.stack.pop()

    def top(self):
        return self.stack[-1]

    def getMin(self):
        return self.min_stack[-1]
tips_and_updates

What interviewers look for: The interviewer is testing whether you can maintain auxiliary state alongside a data structure. The key insight is that the minimum can only change on push/pop, so you can track it incrementally.

linear_scale

Linked List

3 questions

Given the head of a singly linked list, reverse it and return the new head.

Approach

Iterate through the list. At each node, save the next pointer, point the current node's next to the previous node, then advance. Three pointers: prev, curr, next.

Python
Time: O(n)|Space: O(1)
def reverseList(head):
    prev = None
    curr = head
    while curr:
        nxt = curr.next
        curr.next = prev
        prev = curr
        curr = nxt
    return prev
tips_and_updates

What interviewers look for: This is asked constantly as a building block for harder problems (reverse k-group, palindrome linked list). Be able to write it in 30 seconds without thinking. The recursive version is a common follow-up.

Given the heads of two sorted linked lists, merge them into one sorted list.

Approach

Use a dummy head node. Compare the heads of both lists, attach the smaller one to the result, and advance that list's pointer. When one list is exhausted, attach the remainder of the other.

Python
Time: O(n + m)|Space: O(1)
def mergeTwoLists(l1, l2):
    dummy = ListNode()
    curr = dummy
    while l1 and l2:
        if l1.val <= l2.val:
            curr.next = l1
            l1 = l1.next
        else:
            curr.next = l2
            l2 = l2.next
        curr = curr.next
    curr.next = l1 or l2
    return dummy.next
tips_and_updates

What interviewers look for: The dummy node trick avoids special-casing the first element. Master this pattern because it appears in many linked list problems. The follow-up is Merge K Sorted Lists.

Given the head of a linked list, determine if the list has a cycle.

Approach

Floyd's cycle detection: use a slow pointer (moves 1 step) and a fast pointer (moves 2 steps). If there's a cycle, they'll eventually meet. If fast reaches null, there's no cycle.

Python
Time: O(n)|Space: O(1)
def hasCycle(head):
    slow = fast = head
    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True
    return False
tips_and_updates

What interviewers look for: The follow-up is finding where the cycle starts (Linked List Cycle II). After slow and fast meet, reset one pointer to head and move both one step at a time. Where they meet is the cycle start. Know the proof.

account_tree

Trees

8 questions

Given the root of a binary tree, invert it (mirror it) and return the root.

Approach

Recursively swap the left and right children of every node. Base case: if the node is null, return null. The swap happens at every level.

Python
Time: O(n)|Space: O(h),h is tree height
def invertTree(root):
    if not root:
        return None
    root.left, root.right = root.right, root.left
    invertTree(root.left)
    invertTree(root.right)
    return root
tips_and_updates

What interviewers look for: This is simple but interviewers use it to check your comfort with tree recursion. Be able to trace through it and explain the call stack. The iterative BFS version using a queue is a good follow-up to offer.

Given the root of a binary tree, return its maximum depth (number of nodes along the longest path from root to a leaf).

Approach

Recursively compute the depth of left and right subtrees. The depth of the current node is 1 + max(left depth, right depth). Base case: null node has depth 0.

Python
Time: O(n)|Space: O(h)
def maxDepth(root):
    if not root:
        return 0
    return 1 + max(maxDepth(root.left), maxDepth(root.right))
tips_and_updates

What interviewers look for: Offer both recursive and iterative (BFS with level counting) solutions. The interviewer might ask about the worst case for recursion (skewed tree, O(n) stack depth).

25

Same Tree

Easy

Given the roots of two binary trees, check if they are structurally identical and have the same node values.

Approach

Recursively compare both trees. Two trees are the same if both roots are null, or both roots have the same value and their left subtrees are the same and their right subtrees are the same.

Python
Time: O(n)|Space: O(h)
def isSameTree(p, q):
    if not p and not q:
        return True
    if not p or not q:
        return False
    return (p.val == q.val and
            isSameTree(p.left, q.left) and
            isSameTree(p.right, q.right))
tips_and_updates

What interviewers look for: This is a building block for Subtree of Another Tree. The pattern of simultaneous tree traversal comes up often. Be comfortable with the three base cases: both null, one null, values differ.

Given a BST and two nodes p and q, find their lowest common ancestor (the deepest node that is an ancestor of both).

Approach

Exploit the BST property. If both p and q are smaller than root, LCA is in the left subtree. If both are larger, it's in the right. Otherwise, root is the LCA (the split point).

Python
Time: O(h)|Space: O(1)
def lowestCommonAncestor(root, p, q):
    while root:
        if p.val < root.val and q.val < root.val:
            root = root.left
        elif p.val > root.val and q.val > root.val:
            root = root.right
        else:
            return root
tips_and_updates

What interviewers look for: Emphasize that you're using the BST property for O(h) time, not a general tree approach. The follow-up is LCA of a general binary tree (which requires a different technique).

Given the root of a binary tree, return the level order traversal (values grouped by level, left to right).

Approach

BFS with a queue. Process all nodes at the current level before moving to the next. At each level, record the number of nodes in the queue, process that many, and add their children.

Python
Time: O(n)|Space: O(n)
def levelOrder(root):
    if not root:
        return []
    result = []
    queue = [root]
    while queue:
        level = []
        for _ in range(len(queue)):
            node = queue.pop(0)
            level.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        result.append(level)
    return result
tips_and_updates

What interviewers look for: Use collections.deque for O(1) popleft in production code. The pattern of processing level-by-level (tracking queue size) is the core BFS tree pattern. Many tree problems reduce to this.

Given the root of a binary tree, determine if it is a valid BST (left subtree values < root < right subtree values, at every node).

Approach

Recursively validate with min/max bounds. Each node must fall within a valid range. When going left, update the upper bound. When going right, update the lower bound.

Python
Time: O(n)|Space: O(h)
def isValidBST(root, lo=float('-inf'), hi=float('inf')):
    if not root:
        return True
    if root.val <= lo or root.val >= hi:
        return False
    return (isValidBST(root.left, lo, root.val) and
            isValidBST(root.right, root.val, hi))
tips_and_updates

What interviewers look for: The common mistake is only checking left.val < root.val < right.val. That's not enough because a node deep in the left subtree could violate the root's constraint. The bounds approach catches this.

Design an algorithm to serialize a binary tree to a string and deserialize it back to the tree.

Approach

Preorder traversal for serialization. Use a marker (like 'N') for null nodes. For deserialization, split the string and use a queue/iterator. Consume the first element: if it's null, return null. Otherwise, create a node and recursively build left and right subtrees.

Python
Time: O(n)|Space: O(n)
class Codec:
    def serialize(self, root):
        vals = []
        def dfs(node):
            if not node:
                vals.append('N')
                return
            vals.append(str(node.val))
            dfs(node.left)
            dfs(node.right)
        dfs(root)
        return ','.join(vals)

    def deserialize(self, data):
        vals = iter(data.split(','))
        def dfs():
            val = next(vals)
            if val == 'N':
                return None
            node = TreeNode(int(val))
            node.left = dfs()
            node.right = dfs()
            return node
        return dfs()
tips_and_updates

What interviewers look for: Preorder with null markers is the cleanest approach. Using an iterator for deserialization is elegant. The interviewer may ask why preorder works but inorder alone doesn't (you need null markers or a second traversal).

Given the root of a BST and an integer k, return the kth smallest value in the tree.

Approach

In-order traversal of a BST visits nodes in sorted order. Do an iterative in-order traversal using a stack, counting nodes visited. When you've visited k nodes, that's your answer.

Python
Time: O(H + k) where H is tree height|Space: O(H)
def kthSmallest(root, k):
    stack = []
    curr = root
    while stack or curr:
        while curr:
            stack.append(curr)
            curr = curr.left
        curr = stack.pop()
        k -= 1
        if k == 0:
            return curr.val
        curr = curr.right
tips_and_updates

What interviewers look for: The iterative in-order is more efficient than collecting all values because you stop early. The follow-up is: what if the BST is modified frequently and you need kth smallest often? (Augment each node with a subtree count.)

stacked_line_chart

Heap / Priority Queue

2 questions

Given an array of k sorted linked lists, merge them into one sorted linked list.

Approach

Use a min-heap. Push the head of each list into the heap. Pop the smallest, add it to the result, and push its next node. Repeat until the heap is empty.

Python
Time: O(n log k),n total elements, k lists|Space: O(k)
import heapq

def mergeKLists(lists):
    heap = []
    for i, l in enumerate(lists):
        if l:
            heapq.heappush(heap, (l.val, i, l))
    dummy = curr = ListNode()
    while heap:
        val, i, node = heapq.heappop(heap)
        curr.next = node
        curr = curr.next
        if node.next:
            heapq.heappush(heap, (node.next.val, i, node.next))
    return dummy.next
tips_and_updates

What interviewers look for: The index i in the tuple breaks ties when values are equal (nodes aren't comparable). This is a detail interviewers notice. The divide-and-conquer approach (merge pairs) is an alternative worth mentioning.

Design a data structure that supports adding integers and finding the median of all added elements efficiently.

Approach

Maintain two heaps: a max-heap for the lower half and a min-heap for the upper half. Balance them so they differ in size by at most 1. The median is either the top of the larger heap or the average of both tops.

Python
Time: O(log n) add, O(1) median|Space: O(n)
import heapq

class MedianFinder:
    def __init__(self):
        self.lo = []  # max-heap (negated)
        self.hi = []  # min-heap

    def addNum(self, num):
        heapq.heappush(self.lo, -num)
        heapq.heappush(self.hi, -heapq.heappop(self.lo))
        if len(self.hi) > len(self.lo):
            heapq.heappush(self.lo, -heapq.heappop(self.hi))

    def findMedian(self):
        if len(self.lo) > len(self.hi):
            return -self.lo[0]
        return (-self.lo[0] + self.hi[0]) / 2
tips_and_updates

What interviewers look for: Python only has min-heaps, so negate values for the max-heap. The balancing logic is the core of this problem. Walk through a few insertions to show the interviewer it stays balanced.

hub

Graphs

4 questions

Given a 2D grid of '1' (land) and '0' (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent land horizontally or vertically.

Approach

Iterate through the grid. When you find a '1', increment the island count and BFS/DFS to mark all connected land as visited (change to '0'). This way each island is counted exactly once.

Python
Time: O(m × n)|Space: O(m × n) worst case for recursion stack
def numIslands(grid):
    count = 0
    for i in range(len(grid)):
        for j in range(len(grid[0])):
            if grid[i][j] == '1':
                count += 1
                dfs(grid, i, j)
    return count

def dfs(grid, i, j):
    if i < 0 or j < 0 or i >= len(grid) or j >= len(grid[0]) or grid[i][j] != '1':
        return
    grid[i][j] = '0'
    dfs(grid, i+1, j)
    dfs(grid, i-1, j)
    dfs(grid, i, j+1)
    dfs(grid, i, j-1)
tips_and_updates

What interviewers look for: This is THE introductory graph problem. If you modify the input grid (changing '1' to '0'), mention that to the interviewer and offer a visited set alternative if they prefer not to mutate input.

34

Clone Graph

Medium

Given a reference to a node in a connected undirected graph, return a deep copy of the graph.

Approach

BFS or DFS with a hash map from original node to its clone. When visiting a node, create its clone and store it in the map. For each neighbor, clone it if not already cloned, then add the clone to the current clone's neighbors.

Python
Time: O(V + E)|Space: O(V)
def cloneGraph(node):
    if not node:
        return None
    clones = {node: Node(node.val)}
    queue = [node]
    while queue:
        curr = queue.pop(0)
        for neighbor in curr.neighbors:
            if neighbor not in clones:
                clones[neighbor] = Node(neighbor.val)
                queue.append(neighbor)
            clones[curr].neighbors.append(clones[neighbor])
    return clones[node]
tips_and_updates

What interviewers look for: The map serves double duty: it tracks visited nodes AND stores clones. This avoids infinite loops in cyclic graphs. Interviewers test whether you handle cycles correctly.

Given n courses and prerequisites, determine if you can finish all courses (detect if the prerequisite graph has a cycle).

Approach

This is cycle detection in a directed graph. Build an adjacency list, then do DFS with three states per node: unvisited, in-progress, visited. If you reach an in-progress node during DFS, there's a cycle.

Python
Time: O(V + E)|Space: O(V + E)
def canFinish(numCourses, prerequisites):
    graph = [[] for _ in range(numCourses)]
    for course, pre in prerequisites:
        graph[course].append(pre)
    # 0=unvisited, 1=in-progress, 2=done
    state = [0] * numCourses

    def hasCycle(node):
        if state[node] == 1:
            return True
        if state[node] == 2:
            return False
        state[node] = 1
        for neighbor in graph[node]:
            if hasCycle(neighbor):
                return True
        state[node] = 2
        return False

    return not any(hasCycle(i) for i in range(numCourses))
tips_and_updates

What interviewers look for: Explain the three-state approach clearly: unvisited, in-progress (on current DFS path), and done. The in-progress state is what detects back edges (cycles). Kahn's algorithm (BFS topological sort) is an alternative worth mentioning.

Given an m×n island height map, find all cells where water can flow to both the Pacific (top/left borders) and Atlantic (bottom/right borders) oceans.

Approach

Reverse the problem. Instead of checking where water flows from each cell, start from the ocean borders and DFS/BFS uphill to find all cells that can reach each ocean. The answer is the intersection of both sets.

Python
Time: O(m × n)|Space: O(m × n)
def pacificAtlantic(heights):
    if not heights:
        return []
    rows, cols = len(heights), len(heights[0])
    pacific, atlantic = set(), set()

    def dfs(r, c, visited, prev_height):
        if (r, c) in visited or r < 0 or c < 0 or r >= rows or c >= cols:
            return
        if heights[r][c] < prev_height:
            return
        visited.add((r, c))
        for dr, dc in [(1,0),(-1,0),(0,1),(0,-1)]:
            dfs(r+dr, c+dc, visited, heights[r][c])

    for c in range(cols):
        dfs(0, c, pacific, 0)
        dfs(rows-1, c, atlantic, 0)
    for r in range(rows):
        dfs(r, 0, pacific, 0)
        dfs(r, cols-1, atlantic, 0)
    return list(pacific & atlantic)
tips_and_updates

What interviewers look for: The key insight is reversing the direction. Instead of O(m²n²) checking every cell, you do two passes from the borders. This is a pattern that appears in many grid problems. Explain the reversal clearly.

auto_awesome

Dynamic Programming

7 questions

You are climbing a staircase with n steps. Each time you can climb 1 or 2 steps. How many distinct ways can you reach the top?

Approach

This is the Fibonacci sequence. dp[i] = dp[i-1] + dp[i-2]. You can reach step i either from step i-1 (one step) or step i-2 (two steps). Only need two variables, not a full array.

Python
Time: O(n)|Space: O(1)
def climbStairs(n):
    if n <= 2:
        return n
    a, b = 1, 2
    for _ in range(3, n + 1):
        a, b = b, a + b
    return b
tips_and_updates

What interviewers look for: This is the canonical intro to DP. The interviewer is checking whether you recognize the Fibonacci pattern and can optimize from O(n) space to O(1). If you jump to O(1) immediately, explain why it works.

38

House Robber

Medium

Given an array of non-negative integers representing money at each house, find the maximum you can rob without robbing two adjacent houses.

Approach

At each house, you either rob it (add its value to the best from two houses back) or skip it (take the best from the previous house). dp[i] = max(dp[i-1], dp[i-2] + nums[i]).

Python
Time: O(n)|Space: O(1)
def rob(nums):
    if len(nums) <= 2:
        return max(nums)
    a, b = nums[0], max(nums[0], nums[1])
    for i in range(2, len(nums)):
        a, b = b, max(b, a + nums[i])
    return b
tips_and_updates

What interviewers look for: The recurrence is the entire problem. State it clearly before coding. The follow-up is House Robber II (circular arrangement) which just runs this twice with different start/end ranges.

39

Coin Change

Medium

Given coin denominations and a target amount, return the minimum number of coins needed to make that amount. Return -1 if impossible.

Approach

Bottom-up DP. dp[i] = minimum coins to make amount i. For each amount from 1 to target, try every coin. dp[i] = min(dp[i], dp[i - coin] + 1) for each valid coin. Initialize dp[0] = 0, rest to infinity.

Python
Time: O(amount × len(coins))|Space: O(amount)
def coinChange(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i:
                dp[i] = min(dp[i], dp[i - coin] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1
tips_and_updates

What interviewers look for: This is the classic unbounded knapsack problem. Be clear about why greedy doesn't work (e.g., coins [1, 3, 4], amount 6: greedy picks 4+1+1, optimal is 3+3). Interviewers test this understanding.

Given an integer array, return the length of the longest strictly increasing subsequence.

Approach

The O(n²) DP: dp[i] = length of LIS ending at index i. For each i, check all j < i where nums[j] < nums[i], and dp[i] = max(dp[j] + 1). The O(n log n) approach uses a patience sorting technique with binary search.

Python
Time: O(n log n)|Space: O(n)
def lengthOfLIS(nums):
    from bisect import bisect_left
    tails = []
    for num in nums:
        pos = bisect_left(tails, num)
        if pos == len(tails):
            tails.append(num)
        else:
            tails[pos] = num
    return len(tails)
tips_and_updates

What interviewers look for: Show the O(n²) solution first, then optimize to O(n log n). The tails array doesn't store the actual LIS, just its length. Interviewers often ask you to explain why replacing elements in tails is correct.

41

Word Break

Medium

Given a string and a dictionary of words, determine if the string can be segmented into a space-separated sequence of dictionary words.

Approach

dp[i] = true if s[0:i] can be segmented. For each position i, check all possible last words: if dp[j] is true and s[j:i] is in the dictionary, then dp[i] = true.

Python
Time: O(n² × k) where k is average word length for hashing|Space: O(n)
def wordBreak(s, wordDict):
    words = set(wordDict)
    dp = [False] * (len(s) + 1)
    dp[0] = True
    for i in range(1, len(s) + 1):
        for j in range(i):
            if dp[j] and s[j:i] in words:
                dp[i] = True
                break
    return dp[len(s)]
tips_and_updates

What interviewers look for: Convert the word list to a set for O(1) lookups. The optimization of checking only substrings up to the max word length is worth mentioning. The follow-up (Word Break II) asks for all possible segmentations.

42

Unique Paths

Medium

A robot starts at the top-left corner of an m×n grid and can only move right or down. How many unique paths exist to the bottom-right corner?

Approach

dp[i][j] = number of paths to reach cell (i,j). Each cell can only be reached from above or from the left: dp[i][j] = dp[i-1][j] + dp[i][j-1]. First row and first column are all 1s (only one way to reach them).

Python
Time: O(m × n)|Space: O(n)
def uniquePaths(m, n):
    dp = [1] * n
    for _ in range(1, m):
        for j in range(1, n):
            dp[j] += dp[j - 1]
    return dp[-1]
tips_and_updates

What interviewers look for: The 1D DP optimization (reusing a single row) is a nice touch. Mention the math solution (combination formula: C(m+n-2, m-1)) as an O(1) space alternative. The follow-up adds obstacles in the grid.

43

Decode Ways

Medium

Given a string of digits, determine the number of ways to decode it where 'A'=1, 'B'=2, ..., 'Z'=26.

Approach

Similar to Climbing Stairs. dp[i] = ways to decode s[0:i]. If s[i-1] is a valid single digit (1-9), add dp[i-1]. If s[i-2:i] forms a valid two-digit number (10-26), add dp[i-2].

Python
Time: O(n)|Space: O(1)
def numDecodings(s):
    if not s or s[0] == '0':
        return 0
    n = len(s)
    a, b = 1, 1  # dp[i-2], dp[i-1]
    for i in range(1, n):
        temp = 0
        if s[i] != '0':
            temp += b
        two = int(s[i-1:i+1])
        if 10 <= two <= 26:
            temp += a
        a, b = b, temp
    return b
tips_and_updates

What interviewers look for: The edge cases make this tricky: '0' is not a valid single digit, '06' is not valid. Walk through examples like '226' (3 ways) and '06' (0 ways) to show you handle them. Interviewers test these cases specifically.

bolt

Greedy

3 questions

Given an integer array, find the subarray with the largest sum and return the sum.

Approach

Kadane's algorithm. Track the current subarray sum. If it goes negative, reset to 0 (start a new subarray). Track the maximum sum seen. One pass, constant space.

Python
Time: O(n)|Space: O(1)
def maxSubArray(nums):
    best = nums[0]
    current = 0
    for n in nums:
        current = max(n, current + n)
        best = max(best, current)
    return best
tips_and_updates

What interviewers look for: Explain the intuition: a negative running sum can never help a future subarray, so we discard it. The divide-and-conquer O(n log n) approach is a common follow-up. Also be ready for: what if you need the actual subarray, not just the sum?

45

Jump Game

Medium

Given an array where nums[i] represents the max jump length from position i, determine if you can reach the last index starting from index 0.

Approach

Track the farthest index you can reach. Iterate through the array. At each position, update the farthest reachable. If your current position ever exceeds the farthest, you're stuck. If farthest reaches the end, return true.

Python
Time: O(n)|Space: O(1)
def canJump(nums):
    farthest = 0
    for i in range(len(nums)):
        if i > farthest:
            return False
        farthest = max(farthest, i + nums[i])
    return True
tips_and_updates

What interviewers look for: Many candidates overcomplicate this with DP. The greedy solution is simpler and faster. Show the DP approach if asked, but lead with greedy. Jump Game II (minimum jumps) is the follow-up.

Given an array of intervals, merge all overlapping intervals and return the non-overlapping intervals.

Approach

Sort intervals by start time. Iterate through them. If the current interval overlaps with the last merged one (start <= previous end), merge by extending the end. Otherwise, add a new interval.

Python
Time: O(n log n)|Space: O(n)
def merge(intervals):
    intervals.sort(key=lambda x: x[0])
    merged = [intervals[0]]
    for start, end in intervals[1:]:
        if start <= merged[-1][1]:
            merged[-1][1] = max(merged[-1][1], end)
        else:
            merged.append([start, end])
    return merged
tips_and_updates

What interviewers look for: The sort is key. Without it, merging is much harder. Clarify edge cases: what if the array is empty? What if all intervals overlap into one? The pattern of sorting + linear scan applies to many interval problems.

fork_right

Backtracking

2 questions

Given an array of distinct integers and a target, return all unique combinations that sum to the target. The same number may be used unlimited times.

Approach

Backtracking with a start index. At each step, either include the current candidate (and stay at the same index since repeats are allowed) or move to the next candidate. Prune when the remaining target goes negative.

Python
Time: O(n^(target/min)) worst case|Space: O(target/min) for recursion depth
def combinationSum(candidates, target):
    result = []
    def backtrack(start, path, remaining):
        if remaining == 0:
            result.append(path[:])
            return
        for i in range(start, len(candidates)):
            if candidates[i] > remaining:
                continue
            path.append(candidates[i])
            backtrack(i, path, remaining - candidates[i])
            path.pop()
    candidates.sort()
    backtrack(0, [], target)
    return result
tips_and_updates

What interviewers look for: Sorting the candidates first lets you prune early (break when candidate > remaining). The start index prevents duplicate combinations. Walk through a small example to show the recursion tree.

48

Word Search

Medium

Given a 2D board of characters and a word, determine if the word exists in the grid by connecting adjacent cells (no cell reused).

Approach

DFS from every cell. At each step, check if the current cell matches the current character. Mark the cell as visited, explore all four directions for the next character, then unmark (backtrack).

Python
Time: O(m × n × 4^L) where L is word length|Space: O(L) for recursion stack
def exist(board, word):
    rows, cols = len(board), len(board[0])
    def dfs(r, c, idx):
        if idx == len(word):
            return True
        if r < 0 or c < 0 or r >= rows or c >= cols or board[r][c] != word[idx]:
            return False
        temp = board[r][c]
        board[r][c] = '#'
        found = (dfs(r+1,c,idx+1) or dfs(r-1,c,idx+1) or
                 dfs(r,c+1,idx+1) or dfs(r,c-1,idx+1))
        board[r][c] = temp
        return found
    for r in range(rows):
        for c in range(cols):
            if dfs(r, c, 0):
                return True
    return False
tips_and_updates

What interviewers look for: The key optimization is marking cells in-place rather than using a visited set. Mention that this modifies the board temporarily (backtracking restores it). Pruning is critical for performance.

architecture

Design

2 questions

49

LRU Cache

Medium

Design a data structure that follows the constraints of a Least Recently Used cache. get and put must each run in O(1) average time.

Approach

Combine a hash map with a doubly linked list. The map gives O(1) key lookups. The list maintains access order. On get/put, move the node to the front. On capacity overflow, remove the tail node and delete its key from the map.

Python
Time: O(1) for get and put|Space: O(capacity)
class Node:
    def __init__(self, k=0, v=0):
        self.key, self.val = k, v
        self.prev = self.next = None

class LRUCache:
    def __init__(self, capacity):
        self.cap = capacity
        self.cache = {}
        self.head, self.tail = Node(), Node()
        self.head.next, self.tail.prev = self.tail, self.head

    def _remove(self, node):
        node.prev.next, node.next.prev = node.next, node.prev

    def _insert(self, node):
        node.prev, node.next = self.head, self.head.next
        self.head.next.prev = node
        self.head.next = node

    def get(self, key):
        if key in self.cache:
            node = self.cache[key]
            self._remove(node)
            self._insert(node)
            return node.val
        return -1

    def put(self, key, value):
        if key in self.cache:
            self._remove(self.cache[key])
        node = Node(key, value)
        self.cache[key] = node
        self._insert(node)
        if len(self.cache) > self.cap:
            lru = self.tail.prev
            self._remove(lru)
            del self.cache[lru.key]
tips_and_updates

What interviewers look for: This is one of the most frequently asked medium problems. Practice writing the doubly linked list operations cleanly. The sentinel head/tail nodes eliminate null checks. Be ready to trace through put operations at capacity.

Implement a trie with insert, search, and startsWith methods.

Approach

Each trie node has a children map (char to node) and a boolean marking end of word. Insert walks the trie creating nodes as needed. Search walks the trie and checks the end marker. startsWith just checks if the prefix path exists.

Python
Time: O(m) per operation where m is word/prefix length|Space: O(total characters across all words)
class TrieNode:
    def __init__(self):
        self.children = {}
        self.is_end = False

class Trie:
    def __init__(self):
        self.root = TrieNode()

    def insert(self, word):
        node = self.root
        for c in word:
            if c not in node.children:
                node.children[c] = TrieNode()
            node = node.children[c]
        node.is_end = True

    def search(self, word):
        node = self._find(word)
        return node is not None and node.is_end

    def startsWith(self, prefix):
        return self._find(prefix) is not None

    def _find(self, s):
        node = self.root
        for c in s:
            if c not in node.children:
                return None
            node = node.children[c]
        return node
tips_and_updates

What interviewers look for: Extracting a helper _find method that returns the node (or null) makes search and startsWith clean. Interviewers notice code organization. The follow-up is often: add a delete method, or design an autocomplete system.

Knowing the questions is step one

Solving them under pressure while explaining your thinking is what gets you the offer. Practice with an AI interviewer that simulates the real thing.

Try a free mock interviewarrow_forward

Browse technical interview questions by company

See the exact problems each company asks. 459+ companies, 1,601+ reported questions.

How to prepare for technical interview questions

Work topic by topic. Finish all array problems before moving to trees. This builds pattern recognition within each category. When you see a new problem in an interview, you'll recognize which pattern it belongs to.

Understand, don't memorize. Companies rotate specific problems, but the underlying patterns stay the same. If you understand why a hash map solves Two Sum, you can apply that pattern to dozens of variations you've never seen.

Practice the interview, not just the problem. Solving a problem in silence is not the same as solving it while explaining your approach to an interviewer. Once you can solve these, start running AI mock interviews to practice under real conditions.

For a structured plan, try the Blind 75, Grind 75, or NeetCode 150.

Frequently Asked Questions

What are the most common technical interview questions?add

The most common technical interview questions involve arrays, hash maps, trees, graphs, and dynamic programming. Two Sum, Valid Parentheses, Merge Intervals, LRU Cache, and Number of Islands appear across nearly every top tech company. This page covers the 50 most frequently asked with full solutions.

How many technical interview questions should I prepare?add

Most successful candidates prepare 50 to 150 problems. The key is covering all major patterns. If you deeply understand the 50 problems on this page and can explain your approach clearly under pressure, you'll be prepared for the majority of technical interviews at any company.

What topics do technical interview questions cover?add

Arrays, strings, hash maps, two pointers, sliding window, binary search, linked lists, stacks, trees, heaps, graphs, dynamic programming, greedy algorithms, and backtracking. Most companies weight arrays and dynamic programming the heaviest.

How should I practice technical interview questions?add

Work topic by topic. Learn the pattern, then practice variations. Once you can solve problems consistently, start doing mock interviews to practice explaining your thinking under time pressure. That's the gap between solving and passing.

Do FAANG companies ask the same technical interview questions?add

There's significant overlap. Two Sum, LRU Cache, and Number of Islands appear at nearly every major company. The difference is in follow-up depth. Google pushes harder on optimization, Amazon focuses on scalability, and Meta emphasizes speed.