Question Banks
DSA Advanced 50 Pattern Problems
50 problems from NeetCode 150 and Striver SDE Sheet organized by pattern. Complete Python solutions with complexities and follow-ups.
Problems P26–P75 organized by algorithmic pattern. Covers NeetCode 150 and Striver SDE Sheet essentials. Each tagged with difficulty level for calibration.
Arrays & Hashing
P26: Contains Duplicate Easy · SDE1
Problem: Given integer array, return true if any value appears at least twice.
Approach: Hash set. If element already in set, return true.
def contains_duplicate(nums):
return len(nums) != len(set(nums)) Complexity: O(n) time, O(n) space.
Follow-up: Contains Duplicate II within distance k? Use sliding window hash set of size k.
P27: Valid Anagram Easy · SDE1
Problem: Given two strings s and t, return true if t is an anagram of s.
from collections import Counter
def is_anagram(s, t):
return Counter(s) == Counter(t) Complexity: O(n) time, O(1) space (26 chars max).
Follow-up: What if inputs contain Unicode? → Counter still works. What about streaming? → Single count array, increment/decrement.
P28: Group Anagrams Medium · SDE2
Problem: Given array of strings, group anagrams together.
Approach: Use sorted string as key in hash map.
from collections import defaultdict
def group_anagrams(strs):
groups = defaultdict(list)
for s in strs:
key = ''.join(sorted(s))
groups[key].append(s)
return list(groups.values()) Complexity: O(n · k log k) time where k = max string length.
Follow-up: Avoid sorting? → Use tuple of 26 char counts as key: O(n·k).
P29: Top K Frequent Elements Medium · SDE2
Problem: Given integer array and k, return the k most frequent elements.
Approach: Bucket sort by frequency. Index = frequency, value = elements with that frequency.
from collections import Counter
def top_k_frequent(nums, k):
count = Counter(nums)
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
return result Complexity: O(n) time, O(n) space.
Follow-up: Streaming data? → Min-heap of size k: O(n log k).
P30: Product of Array Except Self Medium · SDE2
Problem: Return array where answer[i] = product of all elements except nums[i]. No division.
Approach: Prefix products left-to-right, then suffix products right-to-left.
def product_except_self(nums):
n = len(nums)
result = [1] * n
prefix = 1
for i in range(n):
result[i] = prefix
prefix *= nums[i]
suffix = 1
for i in range(n - 1, -1, -1):
result[i] *= suffix
suffix *= nums[i]
return result Complexity: O(n) time, O(1) extra space (output not counted).
Follow-up: Handle zeros? → Count zeros. If >1 zero, all products are 0. If exactly 1, only that position is non-zero.
P31: Longest Consecutive Sequence Medium · SDE2
Problem: Given unsorted array, find length of longest consecutive elements sequence. Must be O(n).
Approach: Hash set. For each number, only start counting if num-1 is NOT in set (start of sequence).
def longest_consecutive(nums):
num_set = set(nums)
longest = 0
for num in num_set:
if num - 1 not in num_set: # start of sequence
length = 1
while num + length in num_set:
length += 1
longest = max(longest, length)
return longest Complexity: O(n) time, O(n) space.
Follow-up: Streaming data with deletions? → Union-Find approach.
Two Pointers
P32: Valid Palindrome Easy · SDE1
Problem: Check if string is palindrome considering only alphanumeric characters (case-insensitive).
def is_palindrome(s):
left, right = 0, len(s) - 1
while left < right:
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return True Complexity: O(n) time, O(1) space.
P33: 3Sum Medium · SDE2
Problem: Find all unique triplets that sum to zero.
Approach: Sort + fix one element + two-pointer on remainder.
def three_sum(nums):
nums.sort()
result = []
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i-1]:
continue
lo, hi = i + 1, len(nums) - 1
while lo < hi:
total = nums[i] + nums[lo] + nums[hi]
if total == 0:
result.append([nums[i], nums[lo], nums[hi]])
while lo < hi and nums[lo] == nums[lo+1]: lo += 1
while lo < hi and nums[hi] == nums[hi-1]: hi -= 1
lo += 1; hi -= 1
elif total < 0:
lo += 1
else:
hi -= 1
return result Complexity: O(n²) time, O(1) extra space.
Follow-up: 3Sum Closest return sum nearest to target. Same pattern, track closest diff.
P34: Container With Most Water Medium · SDE2
Problem: Given heights array, find two lines that form container holding most water.
Approach: Two pointers from edges. Move the shorter side inward (greedy).
def max_area(height):
left, right = 0, len(height) - 1
best = 0
while left < right:
area = min(height[left], height[right]) * (right - left)
best = max(best, area)
if height[left] < height[right]:
left += 1
else:
right -= 1
return best Complexity: O(n) time, O(1) space.
P35: Remove Duplicates from Sorted Array II Medium · SDE2
Problem: Allow at most 2 duplicates in-place. Return new length.
def remove_duplicates(nums):
if len(nums) <= 2:
return len(nums)
slow = 2
for fast in range(2, len(nums)):
if nums[fast] != nums[slow - 2]:
nums[slow] = nums[fast]
slow += 1
return slow Complexity: O(n) time, O(1) space.
P36: 4Sum Medium · SDE3
Problem: Find all unique quadruplets summing to target.
Approach: Sort + fix two elements + two-pointer on rest. Skip duplicates at each level.
def four_sum(nums, target):
nums.sort()
result = []
n = len(nums)
for i in range(n - 3):
if i > 0 and nums[i] == nums[i-1]: continue
for j in range(i+1, n-2):
if j > i+1 and nums[j] == nums[j-1]: continue
lo, hi = j+1, n-1
while lo < hi:
total = nums[i] + nums[j] + nums[lo] + nums[hi]
if total == target:
result.append([nums[i], nums[j], nums[lo], nums[hi]])
while lo < hi and nums[lo] == nums[lo+1]: lo += 1
while lo < hi and nums[hi] == nums[hi-1]: hi -= 1
lo += 1; hi -= 1
elif total < target: lo += 1
else: hi -= 1
return result Complexity: O(n³) time.
Stack
P37: Valid Parentheses Easy · SDE1
Problem: Check if string of brackets is valid.
def is_valid(s):
stack = []
openers = "([" + chr(123)
closers = ")]" + chr(125)
for char in s:
if char in openers:
stack.append(char)
elif char in closers:
if not stack:
return False
idx = closers.index(char)
if stack[-1] != openers[idx]:
return False
stack.pop()
return not stack for char in s:
if char in pairs:
stack.append(pairs[char])
elif not stack or stack.pop() != char:
return False
return not stack
Complexity: O(n) time, O(n) space.
P38: Min Stack Medium · SDE1
Problem: Design stack supporting push, pop, top, and getMin all O(1).
class MinStack:
def __init__(self):
self.stack = []
self.min_stack = []
def push(self, val):
self.stack.append(val)
self.min_stack.append(min(val, self.min_stack[-1] if self.min_stack else val))
def pop(self):
self.stack.pop()
self.min_stack.pop()
def top(self):
return self.stack[-1]
def get_min(self):
return self.min_stack[-1] Complexity: All operations O(1).
Follow-up: O(1) space? → Store difference from min. Negative diff means new min was set.
P39: Evaluate Reverse Polish Notation Medium · SDE2
Problem: Evaluate arithmetic expression in RPN (postfix).
def eval_rpn(tokens):
stack = []
for t in tokens:
if t in '+-*/':
b, a = stack.pop(), stack.pop()
if t == '+': stack.append(a + b)
elif t == '-': stack.append(a - b)
elif t == '*': stack.append(a * b)
else: stack.append(int(a / b)) # truncate toward zero
else:
stack.append(int(t))
return stack[0] Complexity: O(n) time, O(n) space.
P40: Daily Temperatures Medium · SDE2
Problem: Given temps array, return days until warmer temp for each day.
Approach: Monotonic decreasing stack storing indices.
def daily_temperatures(temperatures):
n = len(temperatures)
result = [0] * n
stack = [] # indices of decreasing temps
for i in range(n):
while stack and temperatures[i] > temperatures[stack[-1]]:
j = stack.pop()
result[j] = i - j
stack.append(i)
return result Complexity: O(n) time, O(n) space.
P41: Largest Rectangle in Histogram Hard · SDE3
Problem: Find largest rectangular area in histogram.
Approach: Monotonic increasing stack. For each bar, find how far left/right it can extend.
def largest_rectangle(heights):
stack = [] # indices of increasing heights
max_area = 0
for i, h in enumerate(heights + [0]): # sentinel
while stack and heights[stack[-1]] > h:
height = heights[stack.pop()]
width = i if not stack else i - stack[-1] - 1
max_area = max(max_area, height * width)
stack.append(i)
return max_area Complexity: O(n) time, O(n) space.
Follow-up: Maximal Rectangle in 2D matrix? → Apply histogram approach per row.
Binary Search
P42: Search a 2D Matrix Medium · SDE1
Problem: Each row sorted, first element of each row > last of previous. Search target.
Approach: Treat as 1D sorted array. Binary search on virtual index.
def search_matrix(matrix, target):
m, n = len(matrix), len(matrix[0])
lo, hi = 0, m * n - 1
while lo <= hi:
mid = (lo + hi) // 2
val = matrix[mid // n][mid % n]
if val == target: return True
elif val < target: lo = mid + 1
else: hi = mid - 1
return False Complexity: O(log(m·n)) time.
P43: Koko Eating Bananas Medium · SDE2
Problem: Find minimum eating speed k to finish all piles within h hours.
Approach: Binary search on answer (speed). Check feasibility for each candidate.
import math
def min_eating_speed(piles, h):
lo, hi = 1, max(piles)
while lo < hi:
mid = (lo + hi) // 2
hours = sum(math.ceil(p / mid) for p in piles)
if hours <= h:
hi = mid
else:
lo = mid + 1
return lo Complexity: O(n · log(max)) time.
P44: Find Minimum in Rotated Sorted Array Medium · SDE2
Problem: Find minimum element in a rotated sorted array (no duplicates).
def find_min(nums):
lo, hi = 0, len(nums) - 1
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] > nums[hi]:
lo = mid + 1
else:
hi = mid
return nums[lo] Complexity: O(log n) time.
P45: Median of Two Sorted Arrays Hard · SDE3+
Problem: Find median of two sorted arrays in O(log(min(m,n))).
Approach: Binary search on partition of smaller array. Ensure left halves ≤ right halves.
def find_median(nums1, nums2):
if len(nums1) > len(nums2):
nums1, nums2 = nums2, nums1
m, n = len(nums1), len(nums2)
lo, hi = 0, m
while lo <= hi:
i = (lo + hi) // 2
j = (m + n + 1) // 2 - i
left1 = nums1[i-1] if i > 0 else float('-inf')
right1 = nums1[i] if i < m else float('inf')
left2 = nums2[j-1] if j > 0 else float('-inf')
right2 = nums2[j] if j < n else float('inf')
if left1 <= right2 and left2 <= right1:
if (m + n) % 2:
return max(left1, left2)
return (max(left1, left2) + min(right1, right2)) / 2
elif left1 > right2:
hi = i - 1
else:
lo = i + 1 Complexity: O(log(min(m,n))) time, O(1) space.
P46: Split Array Largest Sum Hard · SDE3+
Problem: Split array into k subarrays minimizing the largest subarray sum.
Approach: Binary search on answer. Check if array can be split into ≤k parts with max sum ≤ mid.
def split_array(nums, k):
def can_split(max_sum):
parts, curr = 1, 0
for num in nums:
if curr + num > max_sum:
parts += 1
curr = num
else:
curr += num
return parts <= k
lo, hi = max(nums), sum(nums)
while lo < hi:
mid = (lo + hi) // 2
if can_split(mid):
hi = mid
else:
lo = mid + 1
return lo Complexity: O(n · log(sum - max)) time.
Linked List
P47: Reverse Linked List Easy · SDE1
def reverse_list(head):
prev, curr = None, head
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
return prev Complexity: O(n) time, O(1) space.
Follow-up: Reverse between positions m and n? → Same 3-pointer trick with boundary bookmarks.
P48: Linked List Cycle (Floyd's) Easy · SDE1
def has_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return False Complexity: O(n) time, O(1) space.
Follow-up: Find the cycle start? → After detection, reset one pointer to head, advance both by 1. They meet at start.
P49: Merge Two Sorted Lists Easy · SDE1
def merge_two_lists(l1, l2):
dummy = curr = ListNode(0)
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 Complexity: O(m+n) time, O(1) space.
P50: Copy List with Random Pointer Medium · SDE2
Approach: Interleave cloned nodes, then set random pointers, then separate lists.
def copy_random_list(head):
if not head: return None
# Interleave: A->A'->B->B'->C->C'
curr = head
while curr:
clone = Node(curr.val, curr.next, None)
curr.next = clone
curr = clone.next
# Set random pointers
curr = head
while curr:
if curr.random:
curr.next.random = curr.random.next
curr = curr.next.next
# Separate lists
curr = head
clone_head = head.next
while curr:
clone = curr.next
curr.next = clone.next
clone.next = clone.next.next if clone.next else None
curr = curr.next
return clone_head Complexity: O(n) time, O(1) extra space.
P51: Reorder List Medium · SDE2
Problem: L0→L1→...→Ln becomes L0→Ln→L1→Ln-1→...
Approach: Find middle → reverse second half → merge alternating.
def reorder_list(head):
# Find middle
slow = fast = head
while fast.next and fast.next.next:
slow = slow.next
fast = fast.next.next
# Reverse second half
prev, curr = None, slow.next
slow.next = None
while curr:
nxt = curr.next
curr.next = prev
prev = curr
curr = nxt
# Merge alternating
first, second = head, prev
while second:
tmp1, tmp2 = first.next, second.next
first.next = second
second.next = tmp1
first, second = tmp1, tmp2 Complexity: O(n) time, O(1) space.
Trees
P52: Invert Binary Tree Easy · SDE1
def invert_tree(root):
if not root: return None
root.left, root.right = invert_tree(root.right), invert_tree(root.left)
return root Complexity: O(n) time, O(h) space.
P53: Lowest Common Ancestor of BST Medium · SDE2
def lowest_common_ancestor(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 Complexity: O(h) time, O(1) space.
Follow-up: General binary tree (not BST)? → Post-order recursion checking both subtrees.
P54: Binary Tree Level Order Traversal Medium · SDE1
from collections import deque
def level_order(root):
if not root: return []
result, queue = [], deque([root])
while queue:
level = []
for _ in range(len(queue)):
node = queue.popleft()
level.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
result.append(level)
return result Complexity: O(n) time, O(n) space.
P55: Validate BST Medium · SDE2
def is_valid_bst(root, lo=float('-inf'), hi=float('inf')):
if not root: return True
if root.val <= lo or root.val >= hi:
return False
return (is_valid_bst(root.left, lo, root.val) and
is_valid_bst(root.right, root.val, hi)) Complexity: O(n) time, O(h) space.
P56: Diameter of Binary Tree Medium · SDE2
def diameter_of_tree(root):
diameter = 0
def height(node):
nonlocal diameter
if not node: return 0
left = height(node.left)
right = height(node.right)
diameter = max(diameter, left + right)
return 1 + max(left, right)
height(root)
return diameter Complexity: O(n) time, O(h) space.
P57: Construct Tree from Preorder + Inorder Medium · SDE3
def build_tree(preorder, inorder):
if not preorder: return None
root_val = preorder[0]
root = TreeNode(root_val)
mid = inorder.index(root_val)
root.left = build_tree(preorder[1:mid+1], inorder[:mid])
root.right = build_tree(preorder[mid+1:], inorder[mid+1:])
return root Complexity: O(n²) naive. O(n) with inorder index hashmap.
Heap / Priority Queue
P58: Task Scheduler Medium · SDE2
Problem: Given tasks with cooldown n, find minimum intervals to complete all.
from collections import Counter
def least_interval(tasks, n):
counts = Counter(tasks)
max_freq = max(counts.values())
max_count = sum(1 for v in counts.values() if v == max_freq)
return max(len(tasks), (max_freq - 1) * (n + 1) + max_count) Complexity: O(n) time, O(1) space (26 chars).
P59: Reorganize String Medium · SDE2
Problem: Rearrange string so no two adjacent characters are same.
import heapq
from collections import Counter
def reorganize_string(s):
counts = Counter(s)
max_heap = [(-cnt, ch) for ch, cnt in counts.items()]
heapq.heapify(max_heap)
result = []
prev = (0, '')
while max_heap:
cnt, ch = heapq.heappop(max_heap)
result.append(ch)
if prev[0] < 0:
heapq.heappush(max_heap, prev)
prev = (cnt + 1, ch)
return ''.join(result) if len(result) == len(s) else "" Complexity: O(n log k) time where k = unique chars.
P60: K Closest Points to Origin Medium · SDE2
import heapq
def k_closest(points, k):
return heapq.nsmallest(k, points, key=lambda p: p[0]**2 + p[1]**2) Complexity: O(n log k) with heap, or O(n) average with quickselect.
P61: Merge K Sorted Lists (Heap) Hard · SDE3
import heapq
def merge_k_lists(lists):
heap = []
for i, lst in enumerate(lists):
if lst:
heapq.heappush(heap, (lst.val, i, lst))
dummy = curr = ListNode(0)
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 Complexity: O(N log k) time where N = total nodes.
P62: Sliding Window Median Hard · Staff
Problem: Return median of each sliding window of size k.
Approach: Two heaps (max + min) with lazy deletion of expired elements.
import heapq
from collections import defaultdict
def median_sliding_window(nums, k):
lo, hi = [], [] # max-heap (neg), min-heap
def balance():
while len(lo) > len(hi) + 1:
heapq.heappush(hi, -heapq.heappop(lo))
while len(hi) > len(lo):
heapq.heappush(lo, -heapq.heappop(hi))
def get_median():
if k % 2:
return -lo[0]
return (-lo[0] + hi[0]) / 2.0
result = []
for i in range(len(nums)):
heapq.heappush(lo, -nums[i])
heapq.heappush(hi, -heapq.heappop(lo))
balance()
if i >= k:
out = nums[i - k]
if out <= -lo[0]:
lo.remove(-out); heapq.heapify(lo)
else:
hi.remove(out); heapq.heapify(hi)
balance()
if i >= k - 1:
result.append(get_median())
return result Complexity: O(nk) due to remove. Optimal: O(n log k) with lazy deletion + size tracking.
Backtracking
P63: Subsets Medium · SDE2
def subsets(nums):
result = []
def backtrack(start, path):
result.append(path[:])
for i in range(start, len(nums)):
path.append(nums[i])
backtrack(i + 1, path)
path.pop()
backtrack(0, [])
return result Complexity: O(n · 2ⁿ) time and space.
P64: Combination Sum Medium · SDE2
Problem: Find all unique combinations that sum to target (can reuse elements).
def combination_sum(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:
break
path.append(candidates[i])
backtrack(i, path, remaining - candidates[i])
path.pop()
candidates.sort()
backtrack(0, [], target)
return result Complexity: O(2^(t/min)) where t=target, min=smallest candidate.
P65: Permutations Medium · SDE2
def permute(nums):
result = []
def backtrack(path, remaining):
if not remaining:
result.append(path[:])
return
for i in range(len(remaining)):
path.append(remaining[i])
backtrack(path, remaining[:i] + remaining[i+1:])
path.pop()
backtrack([], nums)
return result Complexity: O(n! · n) time.
P66: N-Queens Hard · SDE3
def solve_n_queens(n):
result = []
cols = set()
diag1 = set() # row - col
diag2 = set() # row + col
def backtrack(row, board):
if row == n:
result.append([''.join(r) for r in board])
return
for col in range(n):
if col in cols or (row-col) in diag1 or (row+col) in diag2:
continue
cols.add(col); diag1.add(row-col); diag2.add(row+col)
board[row][col] = 'Q'
backtrack(row + 1, board)
board[row][col] = '.'
cols.remove(col); diag1.remove(row-col); diag2.remove(row+col)
board = [['.' for _ in range(n)] for _ in range(n)]
backtrack(0, board)
return result Complexity: O(n!) time.
P67: Sudoku Solver Hard · SDE3+
def solve_sudoku(board):
def is_valid(row, col, num):
for i in range(9):
if board[row][i] == num or board[i][col] == num:
return False
box_r, box_c = 3 * (row // 3), 3 * (col // 3)
for i in range(box_r, box_r + 3):
for j in range(box_c, box_c + 3):
if board[i][j] == num:
return False
return True
def solve():
for i in range(9):
for j in range(9):
if board[i][j] == '.':
for num in '123456789':
if is_valid(i, j, num):
board[i][j] = num
if solve():
return True
board[i][j] = '.'
return False
return True
solve() Complexity: O(9^(empty cells)) worst case, pruning makes it fast in practice.
Graphs
P68: Rotting Oranges Medium · SDE2
Problem: Multi-source BFS how many minutes until all oranges rot?
from collections import deque
def oranges_rotting(grid):
rows, cols = len(grid), len(grid[0])
queue = deque()
fresh = 0
for r in range(rows):
for c in range(cols):
if grid[r][c] == 2: queue.append((r, c))
elif grid[r][c] == 1: fresh += 1
if fresh == 0: return 0
minutes = 0
while queue:
for _ in range(len(queue)):
r, c = queue.popleft()
for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
nr, nc = r+dr, c+dc
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
grid[nr][nc] = 2
fresh -= 1
queue.append((nr, nc))
minutes += 1
return minutes - 1 if fresh == 0 else -1 Complexity: O(m·n) time and space.
P69: Pacific Atlantic Water Flow Medium · SDE2
Approach: DFS from each ocean's border cells. Return intersection.
def pacific_atlantic(heights):
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 [(0,1),(0,-1),(1,0),(-1,0)]:
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) Complexity: O(m·n) time and space.
P70: Graph Valid Tree Medium · SDE2
Problem: Given n nodes and edges, check if it forms a valid tree (connected + no cycles).
def valid_tree(n, edges):
if len(edges) != n - 1:
return False # tree has exactly n-1 edges
parent = list(range(n))
def find(x):
while parent[x] != x:
parent[x] = parent[parent[x]]
x = parent[x]
return x
for u, v in edges:
pu, pv = find(u), find(v)
if pu == pv:
return False # cycle
parent[pu] = pv
return True Complexity: O(n · α(n)) ≈ O(n) time.
P71: Cheapest Flights Within K Stops Medium · SDE3
Approach: Modified Bellman-Ford limited to k+1 iterations.
def find_cheapest_price(n, flights, src, dst, k):
prices = [float('inf')] * n
prices[src] = 0
for _ in range(k + 1):
temp = prices[:]
for u, v, w in flights:
if prices[u] != float('inf'):
temp[v] = min(temp[v], prices[u] + w)
prices = temp
return prices[dst] if prices[dst] != float('inf') else -1 Complexity: O(k · E) time, O(n) space.
P72: Swim in Rising Water Hard · SDE3+
Problem: Find minimum time t to swim from (0,0) to (n-1,n-1). At time t, can visit cells with elevation ≤ t.
Approach: Dijkstra with max-elevation as cost.
import heapq
def swim_in_water(grid):
n = len(grid)
visited = set()
heap = [(grid[0][0], 0, 0)]
while heap:
t, r, c = heapq.heappop(heap)
if (r, c) in visited: continue
visited.add((r, c))
if r == n-1 and c == n-1:
return t
for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
nr, nc = r+dr, c+dc
if 0 <= nr < n and 0 <= nc < n and (nr,nc) not in visited:
heapq.heappush(heap, (max(t, grid[nr][nc]), nr, nc))
return -1 Complexity: O(n² log n) time.
Dynamic Programming
P73: Longest Common Subsequence Medium · SDE2
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] Complexity: O(mn) time, O(mn) space. Optimize to O(min(m,n)) with rolling array.
Follow-up: Print the actual LCS? → Backtrack from dp[m][n].
P74: Coin Change Medium · SDE2
Problem: Minimum coins to make amount. Return -1 if impossible.
def coin_change(coins, amount):
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for x in range(coin, amount + 1):
dp[x] = min(dp[x], dp[x - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1 Complexity: O(amount · n) time, O(amount) space.
Follow-up: Count number of ways (not minimum)? → Change min to sum.
P75: Partition Equal Subset Sum Medium · SDE2
Problem: Can array be partitioned into two subsets with equal sum? (0/1 Knapsack)
def can_partition(nums):
total = sum(nums)
if total % 2: return False
target = total // 2
dp = [False] * (target + 1)
dp[0] = True
for num in nums:
for j in range(target, num - 1, -1):
dp[j] = dp[j] or dp[j - num]
return dp[target] Complexity: O(n · target) time, O(target) space.
Follow-up: Minimize the difference between two subset sums? → Find largest reachable sum ≤ total/2.