SDE2 FAANG Interview Questions: Graphs & Dynamic Programming

Author Image
Sakshi Jhunjhunwala
SDE2 FAANG Interview Questions: Graphs & Dynamic Programming

SDE2 coding rounds are a meaningful step up from SDE1. The problems are harder, the expected solution is more often optimal from the start, and interviewers probe follow-ups more aggressively.

The two topics that separate SDE2 candidates from SDE1 candidates most clearly are graphs and dynamic programming. At Amazon SDE2 (L5), Google L4, and Meta E4, these are not occasional topics, they are expected competencies. Candidates who can't fluently implement BFS, DFS, topological sort, and medium-difficulty DP rarely clear the onsite at this level.

This blog covers the most frequently reported graph and DP problems at SDE2 level, with verified solutions, full complexity analysis, and the reasoning you need to communicate in a live round.

All solutions are in Python. Every solution has been traced through its example.

What SDE2 Coding Rounds Look Like

Amazon SDE2 (L5): 4–5 round onsite. Coding rounds expect medium-hard problems. Graph problems (BFS/DFS, shortest path, topological sort) and DP variants appear frequently. Bar Raiser round evaluates both coding quality and LP signal.

Google L4: One medium problem with 2–3 follow-ups per round, or two medium problems back-to-back. Google expects optimal solutions and complexity stated unprompted. Graph and tree problems dominate.

Meta E4: Two problems per round, 35 minutes each. Speed matters more than at Google. DP and graph problems appear in the second problem slot, typically harder than the first.

Product companies (Razorpay, Swiggy, Flipkart) at senior engineer level: One medium-hard problem per round. Graph and DP problems appear but are slightly more pattern-predictable than FAANG.

The bar across all: arrive at the optimal approach quickly, code it cleanly, handle edge cases without prompting, and explain complexity without being asked.

Part 1: Graph Problems

Graph Representation Used in All Problems Below

python

# Adjacency list - standard for most graph interview problems
graph = {
   0: [1, 2],
   1: [0, 3],
   2: [0],
   3: [1]
}

Problem 1: Number of Islands

Difficulty: Medium. Asked at: Amazon, Google, Meta, one of the most reported graph problems across all FAANG companies at SDE2 level. LeetCode: #200

Problem Statement

Given an m × n grid of '1' (land) and '0' (water), count the number of islands. An island is surrounded by water and formed by connecting adjacent land cells horizontally or vertically.

Example:

Input:
grid = [
 ["1","1","0","0","0"],
 ["1","1","0","0","0"],
 ["0","0","1","0","0"],
 ["0","0","0","1","1"]
]
Output: 3

Approach

Iterate through every cell. When we find a '1' (unvisited land), increment the island count and run DFS to mark all connected land cells as visited (change them to '0' to avoid revisiting). After DFS, that entire island is consumed. Continue scanning.

Solution

python

def num_islands(grid):
   if not grid:
       return 0

   rows, cols = len(grid), len(grid[0])
   count = 0

   def dfs(r, c):
       # Out of bounds or water — stop
       if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] == '0':
           return
       grid[r][c] = '0'  # mark as visited
       dfs(r + 1, c)
       dfs(r - 1, c)
       dfs(r, c + 1)
       dfs(r, c - 1)

   for r in range(rows):
       for c in range(cols):
           if grid[r][c] == '1':
               count += 1
               dfs(r, c)

   return count

Trace Through the Example

Scanning cell by cell:
(0,0)='1' → count=1. DFS marks (0,0),(0,1),(1,0),(1,1) as '0'.
(0,2)='0' → skip
...
(2,2)='1' → count=2. DFS marks (2,2) as '0'.
(3,3)='1' → count=3. DFS marks (3,3),(3,4) as '0'.

Output: 3 ✓

Complexity

Common Follow-up at SDE2

"What if the grid is too large to fit in memory? "Use BFS with an explicit queue instead of recursive DFS, this converts stack memory to heap memory and allows streaming row-by-row. Also mention: for a distributed setting, Union-Find across partitions with border reconciliation.

Problem 2: Clone Graph

Difficulty: Medium. Asked at: Amazon, Meta, tests understanding of graph traversal with object creation. LeetCode: #133

Problem Statement

Given a reference to a node in a connected undirected graph, return a deep copy of the graph. Each node has a value and a list of neighbours.

python

class Node:
   def __init__(self, val=0, neighbors=None):
       self.val = val
       self.neighbors = neighbors if neighbors is not None else []

Example:

Input:  Node 1 connected to Node 2, Node 2 connected to Node 1
Output: A deep copy with new node objects, same structure

Approach

Use DFS with a hash map that maps each original node to its clone. When we visit a node, create its clone immediately and store the mapping. Then recursively clone all neighbours. The hash map prevents infinite loops on cycles.

Solution

python

def clone_graph(node):
   if not node:
       return None

   cloned = {}  # original node -> cloned node

   def dfs(n):
       if n in cloned:
           return cloned[n]
       clone = Node(n.val)
       cloned[n] = clone
       for neighbour in n.neighbors:
           clone.neighbors.append(dfs(neighbour))
       return clone

   return dfs(node)

Complexity

Problem 3: Course Schedule (Topological Sort / Cycle Detection)

Difficulty: Medium. Asked at: Amazon, Google, topological sort is a required SDE2 competency; this problem is the most common entry point. LeetCode: #207

Problem Statement

There are numCourses courses (labelled 0 to numCourses - 1). You are given a list of prerequisites where prerequisites[i] = [a, b] means you must take course b before course a. Return True if it is possible to finish all courses, False if a cycle exists (making it impossible).

Example:

Input:  numCourses=2, prerequisites=[[1,0]]
Output: True  (take 0 then 1)

Input:  numCourses=2, prerequisites=[[1,0],[0,1]]
Output: False  (cycle: 0 requires 1, 1 requires 0)

Approach

Model as a directed graph. The question reduces to: does this graph contain a cycle? Use DFS with three states per node:

If during DFS we reach a node with state 1, we've found a cycle → return False.

Solution

python

def can_finish(numCourses, prerequisites):
   # Build adjacency list
   graph = [[] for _ in range(numCourses)]
   for course, prereq in prerequisites:
       graph[prereq].append(course)

   # 0 = unvisited, 1 = visiting, 2 = visited
   state = [0] * numCourses

   def dfs(node):
       if state[node] == 1:  # cycle detected
           return False
       if state[node] == 2:  # already processed, safe
           return True
       state[node] = 1  # mark as visiting
       for neighbour in graph[node]:
           if not dfs(neighbour):
               return False
       state[node] = 2  # mark as fully processed
       return True

   for course in range(numCourses):
       if not dfs(course):
           return False
   return True

Trace Through the Cycle Example

numCourses=2, prerequisites=[[1,0],[0,1]]
graph[0] = [1], graph[1] = [0]

dfs(0): state[0]=1
 dfs(1): state[1]=1
   dfs(0): state[0]==1 → cycle! return False
 → False
→ False

Output: False ✓

Complexity

Important Follow-up: Course Schedule II (LeetCode #210)

If asked to return the actual order, collect nodes in the order they finish DFS (post-order) and reverse. This gives a valid topological sort.

python

def find_order(numCourses, prerequisites):
   graph = [[] for _ in range(numCourses)]
   for course, prereq in prerequisites:
       graph[prereq].append(course)

   state = [0] * numCourses
   order = []

   def dfs(node):
       if state[node] == 1:
           return False
       if state[node] == 2:
           return True
       state[node] = 1
       for neighbour in graph[node]:
           if not dfs(neighbour):
               return False
       state[node] = 2
       order.append(node)  # add after all descendants processed
       return True

   for course in range(numCourses):
       if not dfs(course):
           return []

   return order[::-1]  # reverse post-order = topological order

Problem 4: Rotting Oranges

Difficulty: Medium. Asked at: Amazon, Google, multi-source BFS; tests whether candidates know BFS is the right tool for "minimum time" grid problems. LeetCode: #994

Problem Statement

You are given an m × n grid. Each cell is:

Every minute, a rotten orange makes all 4-directionally adjacent fresh oranges rot. Return the minimum number of minutes until no fresh orange remains. Return -1 if it is impossible.

Example:

Input:
grid = [
 [2,1,1],
 [1,1,0],
 [0,1,1]
]
Output: 4

Approach

Multi-source BFS starting from all rotten oranges simultaneously. This is the key insight, all rotten oranges rot their neighbours at the same time, so BFS naturally models this. Track the count of fresh oranges; when it reaches zero, we are done. If fresh oranges remain after BFS, return -1.

Solution

python

from collections import deque

def oranges_rotting(grid):
   rows, cols = len(grid), len(grid[0])
   queue = deque()
   fresh_count = 0

   # Initialise queue with all rotten oranges, count fresh ones
   for r in range(rows):
       for c in range(cols):
           if grid[r][c] == 2:
               queue.append((r, c, 0))  # (row, col, time)
           elif grid[r][c] == 1:
               fresh_count += 1

   if fresh_count == 0:
       return 0

   directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
   max_time = 0

   while queue:
       r, c, time = queue.popleft()
       for dr, dc in directions:
           nr, nc = r + dr, c + dc
           if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
               grid[nr][nc] = 2  # rot it
               fresh_count -= 1
               max_time = max(max_time, time + 1)
               queue.append((nr, nc, time + 1))

   return max_time if fresh_count == 0 else -1

Complexity

Part 2: Dynamic Programming Problems

Problem 5: Coin Change

Difficulty: Medium. Asked at: Amazon, Google, Meta, one of the most frequently asked DP problems at SDE2 level; tests unbounded knapsack pattern. LeetCode: #322

Problem Statement

You are given an integer array coins (coin denominations) and an integer amount. Return the minimum number of coins needed to make up that amount. Return -1 if it is not possible.

Example:

Input:  coins = [1, 5, 11], amount = 15
Output: 3  (5 + 5 + 5)

Input:  coins = [2], amount = 3
Output: -1

Note on Example 1: [1,5,11] with amount 15:

Approach

This is the unbounded knapsack pattern, each coin can be used unlimited times. Define dp[i] as the minimum number of coins to make amount i. Base case: dp[0] = 0. For each amount from 1 to amount, try every coin and take the minimum.

Solution

python

def coin_change(coins, amount):
   dp = [float('inf')] * (amount + 1)
   dp[0] = 0  # 0 coins needed to make amount 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

Trace Through coins=[1,5,11], amount=15

dp[0]=0
dp[1]=min(inf, dp[0]+1)=1           (use coin 1)
dp[2]=2, dp[3]=3, dp[4]=4
dp[5]=min(dp[4]+1, dp[0]+1)=1       (use coin 5)
dp[6]=2, dp[7]=3, dp[8]=4, dp[9]=5
dp[10]=min(dp[9]+1, dp[5]+1)=2      (use coin 5 twice)
dp[11]=min(dp[10]+1, dp[6]+1, dp[0]+1)=1  (use coin 11)
dp[12]=2, dp[13]=3, dp[14]=4
dp[15]=min(dp[14]+1, dp[10]+1, dp[4]+1)=min(5,3,5)=3  (use coin 5 three times)

Output: 3 ✓

Complexity

Problem 6: Longest Common Subsequence

Difficulty: Medium. Asked at: Amazon, Google, 2D DP; one of the most asked string DP problems at SDE2 level. LeetCode: #1143

Problem Statement

Given two strings text1 and text2, return the length of their longest common subsequence. A subsequence is a sequence that appears in the same relative order but not necessarily contiguous.

Example:

Input:  text1 = "abcde", text2 = "ace"
Output: 3  (the LCS is "ace")

Approach

Define dp[i][j] as the length of the LCS of text1[:i] and text2[:j].

Recurrence:

Base case: dp[0][j] = 0 and dp[i][0] = 0 (LCS with an empty string is 0).

Solution

python

def longest_common_subsequence(text1, text2):
   m, n = len(text1), len(text2)
   dp = [[0] * (n + 1) for _ in range(m + 1)]

   for i in range(1, m + 1):
       for j in range(1, n + 1):
           if text1[i - 1] == text2[j - 1]:
               dp[i][j] = dp[i - 1][j - 1] + 1
           else:
               dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

   return dp[m][n]

Trace Through text1="abcde", text2="ace"

    ""  a  c  e
""  [ 0, 0, 0, 0 ]
a   [ 0, 1, 1, 1 ]
b   [ 0, 1, 1, 1 ]
c   [ 0, 1, 2, 2 ]
d   [ 0, 1, 2, 2 ]
e   [ 0, 1, 2, 3 ]

dp[5][3] = 3 ✓

Complexity

Space Optimisation Follow-up

python

def longest_common_subsequence_optimised(text1, text2):
   m, n = len(text1), len(text2)
   prev = [0] * (n + 1)

   for i in range(1, m + 1):
       curr = [0] * (n + 1)
       for j in range(1, n + 1):
           if text1[i - 1] == text2[j - 1]:
               curr[j] = prev[j - 1] + 1
           else:
               curr[j] = max(prev[j], curr[j - 1])
       prev = curr

   return prev[n]

Problem 7: House Robber

Difficulty: Medium. Asked at: Amazon, Google, linear DP; extremely high frequency across all FAANG companies. LeetCode: #198

Problem Statement

You are a robber planning to rob houses along a street. Each house has a certain amount of money. Adjacent houses have security systems that trigger if both are robbed on the same night. Given an integer array nums representing the amount of money in each house, return the maximum amount you can rob without triggering the alarm.

Example:

Input:  nums = [2, 7, 9, 3, 1]
Output: 12  (rob houses 0, 2, 4: 2 + 9 + 1 = 12)

Approach

At each house i, we have two choices: rob it (take nums[i] + best from i-2) or skip it (take best from i-1). Define dp[i] as the maximum money robbed from the first i houses.

Recurrence: dp[i] = max(dp[i-1], dp[i-2] + nums[i])

We only ever need the last two values, so we can optimise to O(1) space.

Solution

python

def rob(nums):
   if not nums:
       return 0
   if len(nums) == 1:
       return nums[0]

   prev2 = 0  # dp[i-2]
   prev1 = 0  # dp[i-1]

   for num in nums:
       current = max(prev1, prev2 + num)
       prev2 = prev1
       prev1 = current

   return prev1

Trace Through [2, 7, 9, 3, 1]

prev2=0, prev1=0

num=2: current=max(0, 0+2)=2.  prev2=0, prev1=2
num=7: current=max(2, 0+7)=7.  prev2=2, prev1=7
num=9: current=max(7, 2+9)=11. prev2=7, prev1=11
num=3: current=max(11,7+3)=11. prev2=11, prev1=11
num=1: current=max(11,11+1)=12.prev2=11, prev1=12

Output: 12 ✓

Complexity

Follow-up: House Robber II (LeetCode #213)

Houses are arranged in a circle, the first and last are adjacent. Split into two subproblems: rob houses [0, n-2] or rob houses [1, n-1]. Return the maximum of both.

python

def rob_ii(nums):
   if len(nums) == 1:
       return nums[0]

   def rob_range(houses):
       prev2, prev1 = 0, 0
       for num in houses:
           current = max(prev1, prev2 + num)
           prev2, prev1 = prev1, current
       return prev1

   return max(rob_range(nums[:-1]), rob_range(nums[1:]))

How SDE2 Interviewers Probe These Problems

At SDE2 level, interviewers don't just want a working solution, they expect you to proactively:

Practising Graph and DP Problems Under Real Conditions

Graph and DP problems are the ones where live interview performance diverges most sharply from solo practice performance.

Candidates who've solved Coin Change 20 times alone still blank on the state definition when asked to explain it under pressure. Candidates who've run Number of Islands 15 times still make off-by-one errors in the boundary condition when someone is watching.

At Intervue.io, SDE2 mock sessions are calibrated to the actual difficulty of mid-level FAANG and product company loops. You get structured feedback on your approach naming, state definition clarity, edge case handling, and communication, the signals that determine SDE2 offer decisions.

👉 Book an SDE2 mock session on Intervue.io

FAQs

How hard are graph problems at Amazon SDE2 specifically? Amazon SDE2 OA and interview rounds include medium graph problems, BFS/DFS on grids, shortest path variants, and topological sort. Hard graph problems (Dijkstra, Bellman-Ford) appear occasionally but are not the baseline expectation. BFS/DFS fluency and topological sort are non-negotiable.

Is DP required at the SDE2 level or just a bonus?Required. Amazon, Google, and Meta all include medium DP problems in SDE2 coding rounds. The patterns that appear most: coin change (unbounded knapsack), house robber (linear DP), and LCS-style string DP. Hard DP (interval DP, bitmask DP) is more of an SDE3 expectation.

Should I use BFS or DFS for Number of Islands? Either works for counting connected components. DFS is slightly simpler to write. BFS is preferred when the problem asks for the shortest path or minimum steps, never use DFS for shortest-path problems in unweighted graphs.

What is the most common DP mistake at SDE2 interviews?Jumping to the recurrence before defining the state. The state definition is the foundation, if it's wrong, the recurrence will be wrong. Always say "dp[i] represents..." out loud before writing anything.

How does topological sort differ from regular DFS? Regular DFS visits nodes and processes them when first encountered. Topological sort uses post-order DFS, nodes are added to the result after all their descendants are processed, then the list is reversed. This guarantees dependencies are resolved before the nodes that depend on them.

Summary

The most frequently asked SDE2 graph and DP problems at FAANG and product companies:

Graphs:

Dynamic Programming:

Define the state before writing code. Name the pattern before coding. State complexity unprompted.

👉 Practice SDE2 graph and DP problems on Intervue.io

Author Image
Sakshi Jhunjhunwala
Product Marketing Manager @Intervue.io
Passionate about turning complex products into clear, compelling narratives that drive demand. Deeply focused on positioning, differentiation, and conversion.

Join the Future of Hiring

Find how Intervue can reduce your time-to-hire, enhance candidate insights, and help you scale your engineering team effortlessly.

Book a Demo