Two Sum
Given an array of integers nums and an integer target, return indices of the two numbers that add up to target. Each input has exactly one solution; you may not use the same element twice.
Walk through the array once. For each number x, the value you'd need to pair with it is target - x. Keep a hash map of every number you've already passed, mapping its value to its index. Before storing the current number, check the map for target - x — if it's there, you've found your pair and can return both indices. The map lookup is O(1), which turns the brute-force O(n²) double loop into a single O(n) pass.
target = 9, nums = [2, 7, 11, 15]
i nums[i] need seen so far action
0 2 7 {} store 2 → 0
1 7 2 {2:0} 2 in seen! return [0, 1] ✓Two Sum
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Best Time to Buy and Sell Stock
You are given an array prices where prices[i] is the price of a stock on day i. Choose one day to buy and a later day to sell to maximize profit. Return 0 if no profit is possible.
You can't go back in time — the buy day must come before the sell day. So scan left-to-right while tracking two things: the lowest price seen so far (your best possible buy) and the best profit achievable by selling at today's price (today minus that lowest). Update both at every step. The final answer is the highest profit ever recorded; if prices only fall, it stays at 0.
prices = [7, 1, 5, 3, 6, 4] day price min so far profit (price - min) best 0 7 7 0 0 1 1 1 0 0 2 5 1 4 4 3 3 1 2 4 4 6 1 5 5 ★ 5 4 1 3 5 answer: 5 (buy day 1 at $1, sell day 4 at $6)
Best Time to Buy and Sell Stock
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Contains Duplicate
Given an integer array nums, return true if any value appears at least twice in the array, and false if every element is distinct.
Walk through the array once, adding each element to a hash set. The moment you try to add a value that's already in the set, you've found a duplicate — return true immediately. If you finish the loop without ever colliding, every element was unique. Hash sets give O(1) average-case add and lookup, so the entire pass is O(n).
nums = [1, 2, 3, 1]
i nums[i] set before add result action
0 1 {} new (true) add
1 2 {1} new (true) add
2 3 {1,2} new (true) add
3 1 {1,2,3} duplicate (false) return true ✓Contains Duplicate
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Product of Array Except Self
Given nums, return an array answer where answer[i] equals the product of all elements of nums except nums[i]. Solve in O(n) without using division.
Without using division, you need to know — for each index i — the product of every element to its left and the product of every element to its right; multiplying those two gives the answer. Do it in two passes. Pass 1 (left-to-right): write the running product of everything to the left of i into res[i]. Pass 2 (right-to-left): multiply res[i] by a running product of everything to the right of i. Each cell ends up holding the full product except itself, in O(n) time and O(1) extra space (the output array doesn't count).
nums = [1, 2, 3, 4] pass 1 (left products): i=0 res[0]=1 then left = 1 i=1 res[1]=1 then left = 2 i=2 res[2]=2 then left = 6 i=3 res[3]=6 then left = 24 res = [1, 1, 2, 6] pass 2 (multiply by right products, right→left): i=3 res[3] *= 1 = 6 right = 4 i=2 res[2] *= 4 = 8 right = 12 i=1 res[1] *= 12 = 12 right = 24 i=0 res[0] *= 24 = 24 right = 24 res = [24, 12, 8, 6] ✓
Product of Array Except Self
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Maximum Subarray
Given an integer array nums, find the contiguous subarray with the largest sum and return that sum. (Kadane's algorithm.)
At each index, ask one question: is the running subarray helping me, or hurting me? If the running sum so far is negative, it can only drag down whatever you add next — drop it and start a fresh subarray at the current element. Otherwise keep it and add the current element. Track the best running sum you've ever seen; that's the answer. One linear pass, no extra space.
nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] i nums[i] cur = max(nums[i], cur + nums[i]) best 0 -2 -2 (start) -2 1 1 max(1, -2+1=-1) = 1 1 2 -3 max(-3, 1-3=-2) = -2 1 3 4 max(4, -2+4=2) = 4 4 4 -1 max(-1, 4-1=3) = 3 4 5 2 max(2, 3+2=5) = 5 5 6 1 max(1, 5+1=6) = 6 6 ★ 7 -5 max(-5, 6-5=1) = 1 6 8 4 max(4, 1+4=5) = 5 6 answer: 6 (subarray [4, -1, 2, 1])
Maximum Subarray
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Maximum Product Subarray
Given an integer array nums, find the contiguous non-empty subarray within nums that has the largest product, and return that product. The answer is guaranteed to fit in a 32-bit integer.
If this were sums, you'd just keep a running total and reset whenever it went negative. Products are harder because of one thing: a negative times another negative is a positive — and if both are large in magnitude, the result can be huge. That means a 'bad' running product (very negative) is actually valuable if the next number is also negative, because it can flip into the best answer. So at every index you can't track just one running value — you have to track two: the largest product of any subarray ending at this index, and the smallest (most negative) product of any subarray ending at this index. When you move to the next number `curr`, the best subarray ending there must be one of three things: (1) `curr` by itself, starting a fresh subarray; (2) `curr` multiplied into the previous max, extending a positive streak; (3) `curr` multiplied into the previous min, in case `curr` is negative and flips a deeply-negative streak into a large positive. Take the max of those three for the new running max. Take the min of the exact same three candidates for the new running min — because next iteration, the min you have now might become the biggest answer. Keep a separate `best` variable that takes the max of itself and the running max at every step. That's your answer.
nums = [2, 3, -2, 4] start: max = 2, min = 2, result = 2 i=1, nums[i]=3 candidates: 2*3=6, 2*3=6, 3 max = 6, min = 3, result = 6 i=2, nums[i]=-2 candidates: 6*-2=-12, 3*-2=-6, -2 max = -2, min = -12, result = 6 (notice: min just became very negative — that's the seed for a future flip) i=3, nums[i]=4 candidates: -2*4=-8, -12*4=-48, 4 max = 4, min = -48, result = 6 answer: 6
Maximum Product Subarray
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Find Minimum in Rotated Sorted Array
An ascending sorted array of unique elements has been rotated. Find the minimum element in O(log n).
In a rotated sorted array, the minimum sits exactly at the rotation point. Binary search by comparing the middle element to the right boundary. If nums[m] > nums[r], the rotation point (and minimum) lies somewhere strictly to the right of m, so move l = m + 1. Otherwise the minimum is at m or to its left, so set r = m (don't skip m — it could be the answer). When l and r meet, you've cornered the minimum.
nums = [3, 4, 5, 1, 2] l=0, r=4 m=2, nums[m]=5, nums[r]=2 → 5 > 2, min is right of m → l=3 l=3, r=4 m=3, nums[m]=1, nums[r]=2 → 1 ≤ 2, min is at m or left → r=3 l=3, r=3 → return nums[3] = 1
Find Minimum in Rotated Sorted Array
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Search in Rotated Sorted Array
Given a rotated sorted array of distinct integers and a target, return its index, or -1 if not found. Must run in O(log n).
A rotated sorted array always has at least one half (left or right of mid) that's still in plain sorted order. Figure out which half by comparing nums[l] to nums[m]: if nums[l] ≤ nums[m], the left half [l..m] is sorted; otherwise the right half [m..r] is. Once you know which half is clean, you can decide in O(1) whether the target lies inside it (just compare against its endpoints). Discard the other half and continue. Standard binary search, plus one 'which side is sorted?' check per step.
nums = [4, 5, 6, 7, 0, 1, 2], target = 0
l=0, r=6
m=3, nums[m]=7
left half [4..7] sorted (nums[l]=4 ≤ nums[m]=7)
target 0 NOT in [4, 7) → search right
l=4, r=6
m=5, nums[m]=1
left half [0..1] sorted (0 ≤ 1)
target 0 in [0, 1) → search left
l=4, r=4
m=4, nums[4]=0 == target → return 4 ✓Search in Rotated Sorted Array
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
3Sum
Given nums, return all unique triplets [a,b,c] such that a+b+c == 0. The solution set must not contain duplicate triplets.
Sort the array first — this enables an efficient two-pointer search. Fix one number nums[i] in the outer loop, then in the remaining sorted suffix use two pointers l (just after i) and r (the end) to find pairs that sum to -nums[i]. If the three-sum is too small, advance l; if too big, retreat r; if it hits zero, record the triplet and step both pointers inward. Duplicates are the only tricky part. Because the array is sorted, equal numbers cluster together, so the same triplet can be generated multiple times — both from a repeated nums[i] in the outer loop and from repeated l/r values inside the sweep. The clean way to handle this is to collect triplets into a Set<List<Integer>>: sorting guarantees each triplet appears in the same canonical order, so equal triplets hash identically and the Set silently drops the duplicates. No fiddly skip-while loops needed. Outer loop O(n), each inner sweep O(n) → O(n²) overall.
nums sorted: [-4, -1, -1, 0, 1, 2] i=0, fix -4. need pair summing to 4 in [-1,-1,0,1,2] l=1, r=5: -1+2=1, too small → l++ ... never reaches 4, done i=1, fix -1. need pair summing to 1 in [-1, 0, 1, 2] l=2, r=5: -1+2 = 1 ✓ → add [-1,-1,2]; l=3, r=4 l=3, r=4: 0+1 = 1 ✓ → add [-1, 0, 1]; l=4, r=3 (done) i=2, fix -1 again. need pair summing to 1 in [0, 1, 2] l=3, r=5: 0+2 = 2, too big → r-- l=3, r=4: 0+1 = 1 ✓ → add [-1, 0, 1] — Set dedupes it l=4, r=3 (done) i=3, fix 0. pair summing to 0 in [1, 2]: none answer: [[-1,-1,2], [-1,0,1]]
3Sum
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Container With Most Water
Given n non-negative integers representing wall heights, find two lines that with the x-axis form a container holding the most water.
Start with the widest possible container — left pointer at 0, right pointer at n-1. The water area is min(height[l], height[r]) × (r - l). Now shrink: which pointer should you move? Always move the SHORTER wall. Reasoning: the height is capped by the shorter wall, and moving the taller wall inward can only reduce the width without ever raising the height — there's no chance of improvement. Moving the shorter wall at least gives the height a chance to grow. Track the best area as you go.
height = [1, 8, 6, 2, 5, 4, 8, 3, 7]
0 1 2 3 4 5 6 7 8
l=0, r=8: min(1,7)=1 × 8 = 8 (best=8) left shorter → l++
l=1, r=8: min(8,7)=7 × 7 = 49 (best=49) right shorter → r--
l=1, r=7: min(8,3)=3 × 6 = 18 right shorter → r--
l=1, r=6: min(8,8)=8 × 5 = 40 equal, move either → r--
... never improves on 49
answer: 49Container With Most Water
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Sum of Two Integers
Given two integers a and b, return their sum without using the operators + or -. Use bit manipulation: XOR for sum-without-carry, AND+shift for carry.
Reproduce binary addition without using + or -. XOR (^) gives the sum of two bits while ignoring any carry. AND (&) followed by a left shift gives exactly the bits that need to be carried into the next column. Loop: compute the carry, set a = a XOR b (sum so far without carries), then set b = the shifted carry. Repeat until there's nothing left to carry — at that point a holds the final sum.
a = 5 (101), b = 3 (011)
iter a b a^b (a&b)<<1
1 101(5) 011(3) 110(6) 010<<1=100(4)
↓ a=110, b=100
2 110(6) 100(4) 010(2) 100<<1=1000(8)
↓ a=010, b=1000
3 010(2) 1000(8) 1010(10) 000<<1=0
↓ a=1010, b=0
b=0 → return a = 10 ✓ (5+3)Sum of Two Integers
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Number of 1 Bits
Write a function that takes an unsigned integer and returns the number of 1 bits (Hamming weight). Trick: n & (n-1) clears the lowest set bit.
There's a bit-manipulation identity worth memorising: the expression n & (n - 1) always clears the lowest 1-bit of n, leaving every other bit untouched. So if you keep applying that and counting, by the time n becomes 0 you've cleared every 1-bit, and your counter equals the original number of 1-bits. This is faster than checking all 32 bits one by one because you only do work for the bits that are actually set.
n = 11 = 1011₂
iter n n - 1 n & (n - 1) count
1 1011 (11) 1010 (10) 1010 (10) 1
2 1010 (10) 1001 (9) 1000 (8) 2
3 1000 (8) 0111 (7) 0000 (0) 3
↑ n = 0 → return 3Number of 1 Bits
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Counting Bits
Given n, return an array of length n+1 where ans[i] is the number of 1's in the binary representation of i. Solve in O(n).
Notice that i and i >> 1 (i with its lowest bit dropped) have the same number of 1-bits, except for that one bit you might have dropped. So bits(i) = bits(i >> 1) + (i & 1). Build the result array bottom-up: dp[0] = 0, then for each later i compute dp[i] in O(1) from an already-filled smaller index. One pass, no per-number bit counting.
n = 5 i binary i >> 1 i & 1 dp[i] = dp[i>>1] + (i&1) 0 000 - - 0 1 001 0 1 dp[0] + 1 = 1 2 010 1 0 dp[1] + 0 = 1 3 011 1 1 dp[1] + 1 = 2 4 100 2 0 dp[2] + 0 = 1 5 101 2 1 dp[2] + 1 = 2 answer: [0, 1, 1, 2, 1, 2]
Counting Bits
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Missing Number
Given an array nums containing n distinct numbers in [0, n], return the only number in the range that is missing.
The numbers 0 through n add up to n(n+1)/2 — the closed-form sum of the first n+1 integers. Subtract every number actually present in the array from that expected total, and whatever's left is the missing number. One pass, O(1) extra space, no hashing or sorting needed.
nums = [3, 0, 1], n = 3 expected sum = 3 * 4 / 2 = 6 6 - 3 = 3 3 - 0 = 3 3 - 1 = 2 answer: 2
Missing Number
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Reverse Bits
Reverse the bits of a 32-bit unsigned integer. Shift the answer left and OR in the lowest bit of n; then shift n right.
Build the reversed result one bit at a time. On each iteration: shift res left by 1 (making room for a new bit at position 0), then OR in the lowest bit of n. Then unsigned-shift n right by 1 to expose the next bit. After 32 passes, every bit of n has migrated to its mirrored position in res.
(showing the lowest 4 bits for clarity; in real code 32 iterations) n = ...1101 iter res before n & 1 res after (shift+or) n after >>>=1 1 0000 1 0001 ...110 2 0001 0 0010 ...11 3 0010 1 0101 ...1 4 0101 1 1011 ...0 each bit of n ends up at its mirror position: original bit 0 lands at bit 31, bit 1 at bit 30, etc.
Reverse Bits
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Climbing Stairs
You can climb 1 or 2 steps at a time. How many distinct ways can you reach step n? (Fibonacci.)
Think backwards from step n. The very last move you made to land on n was either a 1-step (so you came from step n-1) or a 2-step (you came from n-2). Those are the only two options, and they don't overlap, so the number of ways to reach n equals the ways to reach n-1 plus the ways to reach n-2. That recurrence is exactly the Fibonacci sequence. Build it bottom-up from steps 0 and 1 — you only need to remember the previous two values, so it's O(1) extra space.
n = 5
step: 1 2 3 4 5
ways: 1 2 3 5 8
↑ ↑ ↑
1+2 2+3 3+5
state machine:
a, b = 1, 1 (ways to reach steps 0 and 1)
for i = 2..n:
a, b = b, a + b
return bClimbing Stairs
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Coin Change
Given coin denominations and an amount, return the fewest number of coins needed to make up that amount. Return -1 if impossible.
Define dp[i] = the fewest coins needed to make amount i. Base case: dp[0] = 0. For every i from 1 to amount, try each coin c — if i - c is reachable, then dp[i] could be dp[i-c] + 1. Take the minimum across all coin choices. Initialise the array with a sentinel (amount + 1, larger than any possible answer) so 'unreachable' shows up naturally. At the end, dp[amount] is your answer (or -1 if it's still the sentinel).
coins = [1, 2, 5], amount = 11
dp: [0, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞, ∞]
i=1: try 1 → dp[0]+1 = 1 dp[1]=1
i=2: try 1 → dp[1]+1 = 2; try 2 → dp[0]+1=1 dp[2]=1
i=3: try 1 → dp[2]+1 = 2; try 2 → dp[1]+1=2 dp[3]=2
i=4: try 1 → dp[3]+1 = 3; try 2 → dp[2]+1=2 dp[4]=2
i=5: try 1 → 3; try 2 → 3; try 5 → 1 dp[5]=1
...
i=11: try 1 → dp[10]+1=3; try 2 → dp[9]+1=4; try 5 → dp[6]+1=3
dp[11]=3
answer: 3 (e.g. 5 + 5 + 1)Coin Change
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Longest Increasing Subsequence
Given an integer array nums, return the length of the longest strictly increasing subsequence. Patience sort gives O(n log n).
Maintain an array tails where tails[k] holds the smallest possible tail value of any increasing subsequence of length k+1 seen so far. For each new x, binary-search the leftmost position pos where tails[pos] >= x. If you find one, overwrite tails[pos] with x — you've made an increasing subsequence of that length end with a smaller value, leaving more room to extend later. If x is larger than every tail, append it (longest subsequence grew by 1). The contents of tails aren't a valid LIS, but its length always equals the LIS length. O(n log n) total.
nums = [10, 9, 2, 5, 3, 7, 101, 18] x=10 binary search in [] → pos=0, append. tails=[10] x=9 pos in [10] for ≥9 → pos=0, replace. tails=[9] x=2 pos in [9] → pos=0, replace. tails=[2] x=5 pos in [2] for ≥5 → pos=1, append. tails=[2,5] x=3 pos in [2,5] for ≥3 → pos=1, replace. tails=[2,3] x=7 pos in [2,3] for ≥7 → pos=2, append. tails=[2,3,7] x=101 pos in [2,3,7] for ≥101 → pos=3, append. tails=[2,3,7,101] x=18 pos in [2,3,7,101] for ≥18 → pos=3, replace. tails=[2,3,7,18] answer: tails.length = 4
Longest Increasing Subsequence
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Longest Common Subsequence
Given two strings text1 and text2, return the length of their longest common subsequence (not necessarily contiguous).
Build a table dp where dp[i][j] is the LCS length of a's first i characters and b's first j characters. Recurrence: if the latest characters match (a[i-1] == b[j-1]), dp[i][j] = dp[i-1][j-1] + 1 — extend the previous match. Otherwise, drop one character from either string and take the better option: dp[i][j] = max(dp[i-1][j], dp[i][j-1]). Base row and column are zero (an empty string has no common subsequence). The answer is dp[m][n].
a = "abcde", b = "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 (the LCS is "ace")Longest Common Subsequence
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Word Break
Given string s and a dictionary of words, return true if s can be segmented into a space-separated sequence of dictionary words.
Define dp[i] = true if the prefix s[0..i) can be split entirely into dictionary words. Base case: dp[0] = true (the empty prefix is trivially valid). For each i, try every split point j < i: if dp[j] is true and the substring s[j..i) is in the dictionary, then dp[i] is true as well — break early. Answer is dp[s.length]. Putting the dictionary in a HashSet makes each membership check O(1).
s = "leetcode", wordDict = {"leet", "code"}
dp = [T, F, F, F, F, F, F, F, F] (indices 0..8)
i=1 "l" no split works F
i=2 "le" no F
i=3 "lee" no F
i=4 "leet" dp[0]=T and "leet" in set T
i=5 "leetc" dp[4]=T and "c" in set? no F
i=6, i=7 no F
i=8 "leetcode" dp[4]=T and "code" in set T
answer: dp[8] = trueWord Break
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Combination Sum IV
Given an array of distinct positive integers nums and a target, return the number of possible orderings (sequences) that sum to target.
Despite the name, this counts the number of ordered SEQUENCES of nums that sum to target (so [1,2] and [2,1] are different). Define dp[i] = number of sequences summing to i. Base: dp[0] = 1 (one empty sequence sums to 0). For each total i from 1 to target, sum dp[i - n] over every n in nums — each contributes 'all the ways to make i - n, then append n'. Critically, the OUTER loop is over totals (not coins) — that ordering is what makes sequences count rather than combinations.
nums = [1, 2, 3], target = 4 dp[0] = 1 i=1: dp[0] = 1 dp[1] = 1 i=2: dp[1] + dp[0] = 2 dp[2] = 2 i=3: dp[2] + dp[1] + dp[0] = 4 dp[3] = 4 i=4: dp[3] + dp[2] + dp[1] = 4 + 2 + 1 = 7 dp[4] = 7 answer: 7 sequences: [1,1,1,1], [1,1,2], [1,2,1], [2,1,1], [2,2], [1,3], [3,1]
Combination Sum IV
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
House Robber
Given non-negative integers representing money in each house, return the max you can rob without robbing two adjacent houses.
At each house i you have two choices: rob it (then add nums[i] to whatever you'd best collected up to house i-2 — you can't touch i-1 because it's adjacent) or skip it (keep your best total through house i-1). So dp[i] = max(dp[i-1], dp[i-2] + nums[i]). You only ever need the previous two values, so roll two variables prev and curr instead of allocating a full array.
nums = [2, 7, 9, 3, 1] step nums prev curr next = max(curr, prev + nums) 0 2 0 0 max(0, 0+2) = 2 1 7 0 2 max(2, 0+7) = 7 2 9 2 7 max(7, 2+9) = 11 3 3 7 11 max(11, 7+3) = 11 4 1 11 11 max(11, 11+1) = 12 answer: 12 (rob houses 0, 2, 4 → 2 + 9 + 1)
House Robber
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
House Robber II
Same as House Robber, but the houses are arranged in a circle (first and last are adjacent). Run House Robber on nums[1:] and nums[:-1].
Same recurrence as House Robber, but the houses are arranged in a circle, so the first and last houses are now neighbours and can't both be robbed. Reduce to two simpler linear problems: (a) rob within nums[0..n-2] (last house excluded) and (b) rob within nums[1..n-1] (first house excluded). Each becomes a regular linear House Robber. The answer is the larger of the two. Handle the n == 1 edge case separately (return nums[0]).
nums = [2, 3, 2] (circular: 2 ↔ 3 ↔ 2 ↔ 2) range A: nums[0..1] = [2, 3] linear robber: max takings = 3 range B: nums[1..2] = [3, 2] linear robber: max takings = 3 answer: max(3, 3) = 3 (can't rob both 2's because they're adjacent in the circle)
House Robber II
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Decode Ways
A message of digits encodes letters: 'A'=1 ... 'Z'=26. Return the number of ways to decode the string.
Define dp[i] = number of ways to decode the prefix s[0..i). Base: dp[0] = 1 (empty has one decoding — itself). For each i, look at the last one or two digits. If s[i-1] is '1'..'9', it can stand alone as a letter — add dp[i-1]. If s[i-2..i] forms a number 10..26, it can be one two-letter decoding — add dp[i-2]. Watch for the leading-zero edge case: if s[0] == '0', there are zero valid decodings overall.
s = "226"
dp[0] = 1 (empty)
dp[1] = 1 (just "2")
i=2: s[1]='2' single ok → dp[1] = 1
s[0..2]="22" two-digit (10-26) → dp[0] = 1
dp[2] = 1 + 1 = 2 ("2,2" or "22")
i=3: s[2]='6' single ok → dp[2] = 2
s[1..3]="26" two-digit ok → dp[1] = 1
dp[3] = 2 + 1 = 3 ("2,2,6", "22,6", "2,26")
answer: 3Decode Ways
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Unique Paths
On an m x n grid, a robot at top-left moves only right or down. How many distinct paths reach the bottom-right?
From any cell you can only move right or down, so the number of paths to (i, j) equals paths to (i-1, j) plus paths to (i, j-1). The top row and left column are all 1 (only one way along an edge). You only ever need the current row and the row above, so keep one 1-D array of length n and update it in place: dp[j] += dp[j-1] computes 'paths to (i, j) = paths to (i-1, j) (already in dp[j]) + paths to (i, j-1) (just computed in dp[j-1])'.
m = 3, n = 7
initial dp = [1, 1, 1, 1, 1, 1, 1] (top row)
row i=1: dp[j] += dp[j-1] across j=1..6
dp = [1, 2, 3, 4, 5, 6, 7]
row i=2: dp[j] += dp[j-1] across j=1..6
dp = [1, 3, 6, 10, 15, 21, 28]
answer: dp[6] = 28Unique Paths
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Jump Game
Given nums where nums[i] is the max jump length from index i, can you reach the last index? Greedy: track furthest reach.
Walk left-to-right, tracking 'reach' — the farthest index you can possibly land on so far. At each index i, first check if i > reach; if so, you've hit a position that can't be reached and you're stuck — return false. Otherwise update reach = max(reach, i + nums[i]) (the farthest you can jump from this index). If you make it through the whole loop, you reached every index, including the last one.
nums = [2, 3, 1, 1, 4] i=0 reach=0 i ≤ reach reach = max(0, 0+2) = 2 i=1 reach=2 i ≤ reach reach = max(2, 1+3) = 4 i=2 reach=4 i ≤ reach reach = max(4, 2+1) = 4 i=3 reach=4 i ≤ reach reach = max(4, 3+1) = 4 i=4 reach=4 i ≤ reach reach = max(4, 4+4) = 8 never failed → return true
Jump Game
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Clone Graph
Given a reference to a node in a connected undirected graph, return a deep copy of the graph. Use DFS + a Map from original to clone.
Walk the original graph with DFS, building copies as you go. Keep a HashMap from each original node to its newly created clone — this acts as both the 'visited' marker AND the way to return the same clone whenever you encounter the same original node twice (essential because graphs can have cycles). For each original node: if it's already in the map, return the cached copy. Otherwise create a new node with the same value, store it in the map BEFORE recursing into neighbours (so cycles short-circuit on the second visit), then recursively clone each neighbour and attach.
graph: 1 — 2
| |
4 — 3
dfs(1):
copy1 = {val:1}, map = {1: copy1}
recurse into neighbours [2, 4]:
dfs(2):
copy2 = {val:2}, map += {2: copy2}
neighbours [1, 3]:
dfs(1) → already in map, return copy1
dfs(3) → build copy3, neighbours include 2 (cached) and 4
copy2.neighbors = [copy1, copy3]
dfs(4):
copy4 = {val:4}, similar walk
copy1.neighbors = [copy2, copy4]
return copy1Clone Graph
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Course Schedule
n courses labeled 0..n-1 with prerequisite pairs [a,b] meaning b must precede a. Return true if all courses can be finished (no cycles). Use Kahn's topological sort.
Model courses as nodes and each prerequisite [a, b] as a directed edge b → a (b must come before a). The schedule is feasible iff the graph has no cycles, i.e. you can topologically sort it. Kahn's algorithm: compute each node's in-degree (number of unmet prerequisites). Push every zero-in-degree node into a queue, then repeatedly pop one (mark it done) and decrement the in-degrees of its dependents — any that hit zero are queued. Count how many you process; if it equals n, you finished all courses. If a cycle exists, some nodes never reach zero and stay stuck.
n = 4, prereqs = [[1,0], [2,0], [3,1], [3,2]]
graph: 0 → 1 → 3
0 → 2 → 3
indeg: 0→0, 1→1, 2→1, 3→2
queue: [0], done = 0
pop 0, done=1. decrement 1 (→0, queue), 2 (→0, queue)
queue: [1, 2]
pop 1, done=2. decrement 3 (→1)
pop 2, done=3. decrement 3 (→0, queue)
queue: [3]
pop 3, done=4
done == n → return trueCourse Schedule
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Pacific Atlantic Water Flow
Given an m x n matrix of heights, water flows to a neighbor if its height is <=. Return cells from which water can flow to BOTH the Pacific (top/left) and Atlantic (bottom/right).
Don't simulate water flowing FROM each cell TO an ocean — that's too expensive. Reverse the perspective: pretend water flows uphill in your model. Start a DFS from every cell on the Pacific border (top row + left column) and mark every cell you can reach by only stepping to neighbours of equal-or-greater height. Do the same from the Atlantic border (bottom row + right column). The cells reachable in BOTH searches are exactly those from which water can drain to both oceans. Two passes, O(m·n) total.
heights: 1 2 2 3 5 3 2 3 4 4 2 4 5 3 1 6 7 1 4 5 5 1 1 2 4 Pacific touches the top row and left column. Atlantic touches the bottom row and right column. DFS from each Pacific border cell, climbing to ≥-height neighbours: marks every cell that water from this cell could flow to Pacific FROM. DFS from each Atlantic border cell, same rule. Cells marked by BOTH searches: [[0,4], [1,3], [1,4], [2,2], [3,0], [3,1], [4,0]]
Pacific Atlantic Water Flow
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Number of Islands
Given an m x n grid of '1' (land) and '0' (water), return the number of islands. An island is a group of '1's connected 4-directionally.
Scan the grid cell by cell. Every time you find a '1' you haven't visited yet, that's a new island, so increment your counter. Then 'sink' that whole island by running DFS from that cell, flipping every connected '1' to '0' as you go (4-directional neighbours only). Because you've turned visited land into water, when your outer scan reaches those cells later it'll skip them — so each island is counted exactly once. You don't need a separate visited array; the grid itself records what you've seen.
grid (scan row by row): 1 1 0 1 0 0 0 0 1 (0,0) = '1' → island #1, sink it . . 0 flood: (0,0) → (0,1) → (1,0) . 0 0 0 0 1 continue scanning... reach (2,2) = '1' . . 0 island #2, sink (2,2) . 0 0 0 0 . answer: 2
Number of Islands
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Longest Consecutive Sequence
Given an unsorted array, return the length of the longest consecutive elements sequence. Solve in O(n). Only start counting from sequence starts.
Throw all numbers into a HashSet for O(1) membership checks. Then for each number n in the set, only START counting if n - 1 is NOT in the set — that means n is the smallest of its consecutive run. From there, walk upward (n+1, n+2, ...) while the next number exists, counting the length. The 'only start from sequence starts' rule keeps the inner loop's TOTAL work O(n) across all iterations, even though it looks like nested loops.
nums = [100, 4, 200, 1, 3, 2]
set = {100, 4, 200, 1, 3, 2}
for each n in set:
100 → 99 not in set, start. 100 → 101? no. len=1
4 → 3 IN set, skip (not a start)
200 → 199 not in set, start. 200 → 201? no. len=1
1 → 0 not in set, start. 1, 2, 3, 4 → 5? no. len=4 ★
3 → 2 IN set, skip
2 → 1 IN set, skip
answer: 4Longest Consecutive Sequence
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Alien Dictionary
Given a sorted list of words from an alien language, derive the order of letters. Build a graph from each adjacent word pair and topo-sort.
Compare each pair of adjacent words. The first position where they differ tells you one ordering rule: that character of the earlier word must come before that character of the later word. Treat these as directed edges in a graph over the alphabet. Then topologically sort with Kahn's BFS. Two edge cases: if one word is a prefix of an EARLIER longer word (e.g. 'apple' before 'app'), that's invalid — return empty string. After topological sort, if the result is shorter than the alphabet you collected, there's a cycle — also return empty string.
words = ["wrt", "wrf", "er", "ett", "rftt"] compare adjacent pairs for first differing char: wrt vs wrf → t before f wrf vs er → w before e er vs ett → r before t ett vs rftt → e before r graph: w → e → r → t → f indeg: w=0, e=1, r=1, t=1, f=1 queue: [w] pop w → "w"; indeg e → 0, queue [e] pop e → "we"; indeg r → 0, queue [r] pop r → "wer"; indeg t → 0, queue [t] pop t → "wert"; indeg f → 0, queue [f] pop f → "wertf" answer: "wertf"
Alien Dictionary
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Graph Valid Tree
Given n nodes (0..n-1) and a list of edges, decide if these edges form a valid tree. A valid tree has exactly n-1 edges, is connected, and has no cycles.
An undirected graph on n nodes is a tree iff it has exactly n - 1 edges AND is connected. Equivalently: exactly n - 1 edges AND no cycles. Use Union-Find: walk through edges and union the endpoints. If you ever try to union two nodes that already share a root, you've found a cycle — return false. If you survive every edge with the count check passing too, the graph is connected and acyclic — a valid tree.
n = 5, edges = [[0,1], [0,2], [0,3], [1,4]] #edges = 4 = n - 1 ✓ (passes the count check) parent: [0, 1, 2, 3, 4] [0,1]: roots 0, 1 differ. union → parent[0]=1 [0,2]: find(0)=1, find(2)=2 differ. union → parent[1]=2 [0,3]: find(0)=2, find(3)=3 differ. union → parent[2]=3 [1,4]: find(1)=3, find(4)=4 differ. union → parent[3]=4 never collided → return true
Graph Valid Tree
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Number of Connected Components
Given n nodes and an array of edges in an undirected graph, return the number of connected components. Union-Find: start with n components and decrement on each successful union.
Start with n disconnected nodes — that's n components. Walk through every edge and union its endpoints; each successful union (different roots) merges two components into one, so decrement the counter. Edges between already-connected nodes leave the count alone. The final counter is your answer.
n = 5, edges = [[0,1], [1,2], [3,4]] count = 5, parent = [0, 1, 2, 3, 4] [0,1]: roots 0, 1 differ. union, count = 4 [1,2]: find(1)=1, find(2)=2 differ. union, count = 3 [3,4]: roots 3, 4 differ. union, count = 2 answer: 2
Number of Connected Components
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Insert Interval
Given non-overlapping intervals sorted by start, insert a new interval (merging if necessary) and return the result.
Intervals are already sorted by start. Walk through in three phases. Phase 1: copy any interval that ends strictly before newInterval starts — these don't overlap. Phase 2: any interval whose start is ≤ newInterval's end overlaps — keep merging by extending newInterval's bounds to absorb them. Phase 3: copy any remaining intervals that start after newInterval ends. Push newInterval at the boundary between phases 2 and 3. O(n) total.
intervals = [[1,3], [6,9]], new = [2,5] phase 1 (ends < 2): [1,3]: 3 < 2? no → skip phase 1 phase 2 (starts ≤ 5): [1,3]: starts 1 ≤ 5, overlap. new = [min(2,1), max(5,3)] = [1,5] [6,9]: starts 6 ≤ 5? no, exit phase 2 push new: [1,5] phase 3: [6,9]: copy result: [[1,5], [6,9]]
Insert Interval
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Merge Intervals
Given an array of intervals, merge all overlapping intervals and return the merged list. Sort by start; merge by extending the last interval's end.
First sort the intervals by their start time. Then walk through them in order, maintaining a 'merged so far' list that starts with the first interval. For each new interval, peek at the last interval already in the list: if the new one starts at or before the last one ends, they overlap, so merge them by extending the last interval's end to the larger of the two ends. Otherwise there's a gap, so just append the new interval to the list as its own entry. The sort dominates the runtime at O(n log n); the sweep is O(n).
input: [1,3], [2,6], [8,10], [15,18]
sorted: [1,3], [2,6], [8,10], [15,18] (already)
result = [[1,3]]
[2,6]: 2 ≤ 3 → overlap, extend end to max(3,6) = 6
result: [[1,6]]
[8,10]: 8 > 6 → no overlap, push
result: [[1,6], [8,10]]
[15,18]: 15 > 10 → push
result: [[1,6], [8,10], [15,18]] ✓Merge Intervals
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Non-overlapping Intervals
Given intervals, return the minimum number you must remove so that the rest are non-overlapping. Greedy by earliest end time.
Sort intervals by their END time. Walk through them, keeping track of the most recently kept interval's end. For each interval: if its start is >= the kept end, it doesn't overlap — keep it (update kept end). Otherwise it overlaps with what you've already kept; remove it (increment counter). Sorting by end time guarantees that whenever there's a conflict, removing the later-ending interval leaves the most room for everything that comes next — that's the greedy correctness argument.
intervals = [[1,2], [2,3], [3,4], [1,3]] sorted by end: [[1,2], [2,3], [1,3], [3,4]] end = -∞, removed = 0 [1,2]: start 1 ≥ -∞, keep. end = 2 [2,3]: start 2 ≥ 2, keep. end = 3 [1,3]: start 1 < 3, remove. removed = 1 [3,4]: start 3 ≥ 3, keep. end = 4 answer: 1
Non-overlapping Intervals
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Meeting Rooms
Given an array of meeting time intervals, determine if a person could attend all meetings (no overlaps).
Sort the meetings by start time. Then walk through and check every adjacent pair: if the current meeting starts before the previous one ends, you can't attend both — return false. If you reach the end without conflicts, you can attend them all.
intervals = [[0,30], [5,10], [15,20]] sorted: [[0,30], [5,10], [15,20]] (already) i=1: starts 5 < prev end 30 → CONFLICT → return false
Meeting Rooms
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Meeting Rooms II
Given meeting intervals, return the minimum number of conference rooms needed. Use sorted starts/ends two-pointer or a min-heap of end times.
Pull all start times into one sorted array and all end times into another. Walk a pointer s through the sorted starts. For each start time, ask 'has any meeting ended by now?' by comparing against ends[e]. If not, you need a new room (rooms++). If yes, an existing room frees up — advance the end pointer e instead. The peak number of overlapping meetings (the max value rooms reaches) is your answer.
intervals = [[0,30], [5,10], [15,20]] starts sorted: [0, 5, 15] ends sorted: [10, 20, 30] s=0 starts[0]=0 < ends[0]=10 → rooms=1, s=1 s=1 starts[1]=5 < ends[0]=10 → rooms=2, s=2 s=2 starts[2]=15 ≥ ends[0]=10 → end frees up, e=1, s=3 answer: 2
Meeting Rooms II
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Reverse Linked List
Given the head of a singly linked list, reverse the list and return the new head.
Walk the list once with three pointers. prev is the head of the reversed list you're building (starts as null). curr is the node you're currently flipping. next is a temporary that holds curr's original next pointer so you don't lose the rest of the original list when you overwrite it. At each step: save curr.next into next, set curr.next = prev (this is the actual reversal), then advance prev = curr and curr = next. When curr becomes null you've processed every node, and prev is the new head.
start: 1 → 2 → 3 → 4 → null
prev = null, curr = 1
step next reroute prev curr
1 2 1 → null 1 2
2 3 2 → 1 2 3
3 4 3 → 2 3 4
4 null 4 → 3 4 null
return prev (= 4)
result: 4 → 3 → 2 → 1 → nullReverse Linked List
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Linked List Cycle
Given head of a linked list, determine if it contains a cycle. Floyd's tortoise & hare: slow moves 1, fast moves 2; they meet inside any cycle.
Use two pointers: slow advances one node per step, fast advances two. If the list ends (fast reaches null), there's no cycle. If there IS a cycle, the fast pointer eventually 'laps' the slow one and they meet at some node inside the cycle — at that moment, return true. Constant extra space, no hash set needed.
list with cycle: 1 → 2 → 3 → 4 → 5 → 2 (back to node 2) step slow fast 0 1 1 1 2 3 2 3 5 3 4 3 (fast wrapped through 5→2→3) 4 5 5 ← meet → return true
Linked List Cycle
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Merge Two Sorted Lists
Merge two sorted linked lists and return as a single sorted list, splicing nodes (no new node creation needed).
Allocate a dummy head node so you don't need a special case for the very first node attached. Keep a tail pointer at dummy. Walk both lists with pointers a and b: at each step, splice whichever has the smaller value into tail.next, then advance that pointer and move tail forward. When one list runs out, attach the remainder of the other in one step. Return dummy.next.
a = 1 → 2 → 4 b = 1 → 3 → 4 dummy → ? tail = dummy a.val=1, b.val=1. a ≤ b → tail.next=a, a=2, tail moves a.val=2, b.val=1. a > b → tail.next=b, b=3, tail moves a.val=2, b.val=3. a ≤ b → tail.next=a, a=4, tail moves a.val=4, b.val=3. a > b → tail.next=b, b=4, tail moves a.val=4, b.val=4. a ≤ b → tail.next=a, a=null, tail moves b not null → tail.next = b (= 4) result: 1 → 1 → 2 → 3 → 4 → 4
Merge Two Sorted Lists
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Merge K Sorted Lists
Given an array of k sorted linked lists, merge them into one sorted list. Pairwise merge (divide & conquer) is O(N log k).
Naively merging the lists one by one is O(N·k); we can do O(N log k) by merging in pairs and halving the list count each round. In each round, walk through the lists array merging i with i+1 (or with null if odd), producing half as many lists. Repeat until one list remains. The merge subroutine is just standard 'merge two sorted lists'. Each node is touched O(log k) times across all rounds.
lists = [L1, L2, L3, L4, L5] (k = 5) round 1: merge in pairs M1 = merge(L1, L2) M2 = merge(L3, L4) M3 = merge(L5, null) = L5 lists = [M1, M2, M3] round 2: N1 = merge(M1, M2) N2 = merge(M3, null) = M3 lists = [N1, N2] round 3: P1 = merge(N1, N2) lists = [P1] return P1
Merge K Sorted Lists
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Remove Nth Node From End
Given the head of a linked list, remove the nth node from the end and return the head. Use two pointers: advance fast n+1 steps first.
Use a dummy node before head so removing the actual head isn't a special case. Set both fast and slow pointers at dummy. Advance fast by n + 1 steps. Then move both at the same pace until fast becomes null — at that moment slow sits exactly one node BEFORE the target. Splice it out with slow.next = slow.next.next, return dummy.next.
list: 1 → 2 → 3 → 4 → 5, n = 2 (remove 2nd-from-end = node 4) dummy → 1 → 2 → 3 → 4 → 5 → null advance fast n+1 = 3 steps from dummy: fast: dummy → 1 → 2 → 3 walk both at same pace until fast == null: slow=dummy, fast=3 slow=1, fast=4 slow=2, fast=5 slow=3, fast=null (stop) slow.next = node 4 → splice it out slow.next = slow.next.next → 3.next becomes 5 result: 1 → 2 → 3 → 5
Remove Nth Node From End
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Reorder List
Given L0->L1->...->Ln-1, reorder it to L0->Ln-1->L1->Ln-2->L2->Ln-3->... in place. Steps: find middle, reverse second half, weave the two halves.
Three steps. (1) Find the middle of the list with slow/fast pointers — slow lands on the midpoint when fast hits the end. (2) Detach the second half and reverse it in place. (3) Weave the two halves together: take one node from the front, then one from the (now-reversed) back, alternating, until done. Each phase is O(n) and uses O(1) extra space.
list: 1 → 2 → 3 → 4 → 5 step 1, find middle (slow/fast): slow ends at 3 split: front = 1 → 2 → 3, back = 4 → 5 step 2, reverse back: 5 → 4 step 3, weave: 1.next = 5; 5.next = 2 → 1 → 5 → 2 2.next = 4; 4.next = 3 → 1 → 5 → 2 → 4 3 has no partner left result: 1 → 5 → 2 → 4 → 3
Reorder List
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Set Matrix Zeroes
Given an m x n matrix, if a cell is 0, set its entire row and column to 0 — in place. Use the first row/col as markers; track them separately.
Naively you'd allocate two extra arrays (length m and n) to record which rows and columns to zero out. To do it in O(1) extra space, repurpose the matrix's own first row and first column as those markers. But first you have to remember separately whether the original first row and first column themselves contained any zeros (since you're about to overwrite them as markers). Then walk the rest of the matrix marking, then walk again zeroing based on the markers, and finally zero out the first row and column themselves if needed.
matrix:
1 1 1
1 0 1 ← zero at (1,1)
1 1 1
step 1: scan original first row and first column for zeros:
firstRow = false, firstCol = false
step 2: scan inner cells; for any 0, mark its row and col in row 0 / col 0:
(1,1)=0 → matrix[1][0] = 0, matrix[0][1] = 0
1 0 1
0 0 1
1 1 1
step 3: zero inner cells whose row 0 or col 0 marker is 0:
1 0 1
0 0 0
1 0 1
step 4: zero first row/col if their original flags were true (both false here)
result:
1 0 1
0 0 0
1 0 1Set Matrix Zeroes
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Spiral Matrix
Given an m x n matrix, return all elements in spiral order (right, down, left, up — repeat). Track top/bottom/left/right boundaries.
Maintain four boundary variables: top, bottom, left, right. Repeatedly traverse one layer in four directions: left-to-right along the top row (then top++), top-to-bottom down the right column (then right--), right-to-left along the bottom row (then bottom--), and bottom-to-top up the left column (then left++). After each direction, shrink that boundary inward. Stop when the boundaries cross. Guard the bottom and left passes with extra checks for the case where they collapse to a single row or column.
matrix: 1 2 3 4 5 6 7 8 9 top=0, bot=2, left=0, right=2 right along top row 0: 1, 2, 3 top=1 down right col 2: 6, 9 right=1 left along bot row 2: 8, 7 bot=1 up left col 0: 4 left=1 top=1, bot=1, left=1, right=1 right along top row 1: 5 top=2 (other passes guarded — top > bot, stop) answer: [1, 2, 3, 6, 9, 8, 7, 4, 5]
Spiral Matrix
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Rotate Image
Rotate an n x n matrix 90 degrees clockwise in place. Trick: transpose, then reverse each row.
Rotating a square matrix 90° clockwise can be done with two simple in-place passes. Pass 1, transpose: swap matrix[i][j] with matrix[j][i] for every pair where i < j. This mirrors the matrix across its main diagonal (top-left to bottom-right). Pass 2, reverse each row left-to-right. The composition of those two operations is exactly a 90° clockwise rotation. Sketch it once on a 3×3 grid and the transformation becomes obvious; both passes are O(n²) time and O(1) extra space.
original transpose reverse each row 1 2 3 1 4 7 7 4 1 4 5 6 → 2 5 8 → 8 5 2 7 8 9 3 6 9 9 6 3 (transpose: (read each row swap matrix[i][j] ↔ matrix[j][i] right-to-left) for all i < j)
Rotate Image
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Word Search
Given an m x n grid of letters and a word, return true if the word can be constructed from sequentially adjacent cells. The same cell may not be used twice.
From each cell, try DFS searching for the word starting at index 0. At each step, check bounds, that the cell matches word[k], and that you haven't already used this cell on the current path. To avoid an extra visited array, temporarily overwrite the cell with a sentinel ('#') before recursing into the four neighbours, then restore it after the recursion returns — classic backtracking. If recursion ever succeeds (k reaches word.length), return true; otherwise undo the mark and let the next start cell try.
board: word = "ABCCED"
A B C E
S F C S
A D E E
start at (0,0)='A', match k=0.
mark (0,0)='#', recurse looking for 'B'
(0,1)='B' matches k=1. mark, recurse for 'C'
(0,2)='C' matches k=2. mark, recurse for 'C'
(1,2)='C' matches k=3. mark, recurse for 'E'
(2,2)='E' matches k=4. mark, recurse for 'D'
(2,1)='D' matches k=5. k+1 == word.length → return true ✓Word Search
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Longest Substring Without Repeating Characters
Given a string s, find the length of the longest substring without repeating characters. Sliding window with a Map of last-seen indices.
Use a sliding window defined by two indices l (left edge) and r (right edge), both starting at 0. Also keep a hash map that remembers, for each character, the most recent index where you saw it. Move r forward one step at a time. When you encounter s[r], check the map: if you've seen this character at some position p that's still inside the window (p >= l), the window now contains a duplicate — slide l forward to p + 1 to kick the old occurrence out. Then update the map with s[r]'s new index, and update your best answer with the current window length r - l + 1. Each character is visited at most twice (once by r, once by l), so it's O(n).
s = "abcabcbb"
0 1 2 3 4 5 6 7
r=0 'a' new window [a] len=1
r=1 'b' new window [ab] len=2
r=2 'c' new window [abc] len=3 ★ best
r=3 'a' last at 0 l → 1
window [bca] len=3
r=4 'b' last at 1 l → 2
window [cab] len=3
r=5 'c' last at 2 l → 3
window [abc] len=3
r=6 'b' last at 4 l → 5
window [cb] len=2
r=7 'b' last at 6 l → 7
window [b] len=1
answer: 3Longest Substring Without Repeating Characters
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Longest Repeating Character Replacement
Given string s and integer k, you may replace at most k characters with any letter. Return the length of the longest substring containing the same letter.
Slide a window [l, r] and within it count the frequency of every letter. Track maxCount — the highest frequency in the current window. The minimum number of characters you'd have to change to make the whole window one letter is windowSize - maxCount. If that exceeds k, the window is too big; shrink it by advancing l (and decrementing the leaving character's count). Otherwise it's valid — update your best length. You don't need to recompute maxCount after shrinking; only growing the window can ever beat the current best, and a stale max can't lead to wrong answers.
s = "AABABBA", k = 1
count = [0]×26, l = 0, maxCount = 0, best = 0
r=0 'A': count[A]=1, maxCount=1. win=1, 1-1=0 ≤ 1, best=1
r=1 'A': count[A]=2, maxCount=2. win=2, 2-2=0 ≤ 1, best=2
r=2 'B': count[B]=1, max stays 2. win=3, 3-2=1 ≤ 1, best=3
r=3 'A': count[A]=3, maxCount=3. win=4, 4-3=1 ≤ 1, best=4 ★
r=4 'B': count[B]=2, max stays 3. win=5, 5-3=2 > 1, shrink l→1 (A--)
win=4, 4-3=1 ≤ 1, best=4
r=5 'B': count[B]=3, maxCount=3. win=5, 5-3=2 > 1, shrink l→2 (A--)
win=4, 4-3=1 ≤ 1, best=4
r=6 'A': count[A]=2 (stale max=3). win=5, 5-3=2 > 1, shrink l→3 (B--)
win=4, 4-3=1 ≤ 1, best=4
answer: 4Longest Repeating Character Replacement
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Minimum Window Substring
Given strings s and t, return the minimum window substring of s such that every character in t (including counts) is included in the window. Sliding window + counts.
Build a 'need' map of each character in t with its required count. Slide a window [l, r] over s and a 'window' map of counts inside it. Track 'have' = how many distinct characters have reached their required count. Expand r; whenever a character's window count hits its needed count, have++. Once have equals the number of distinct required chars, the window is feasible — try shrinking from the left to find the smallest such window, updating the best length each time. Keep shrinking until the window stops being feasible (some character drops below its need), then resume expanding r.
s = "ADOBECODEBANC", t = "ABC"
need = {A:1, B:1, C:1}, required = 3
walk r forward, expanding the window:
... once r=5 the window covers "ADOBEC"
A:1, B:1, C:1 all met → have=3
shrink l:
l=0 'A' leaves → A count drops to 0 < need → have=2
best so far: "ADOBEC", len=6
continue r:
... eventually at r=12 the window covers "CODEBANC"
shrink: window becomes "BANC" — A:1, B:1, C:1 still all met
best updated: "BANC", len=4 ★
answer: "BANC"Minimum Window Substring
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Valid Anagram
Given two strings s and t, return true if t is an anagram of s. Compare character counts.
If the lengths differ, they can't be anagrams. Otherwise walk both strings together with one int[26] array — increment for each char of s, decrement for each char of t at the same loop index. After the pass, if the two strings are anagrams, every counter ended up at zero. Any non-zero entry signals a mismatch.
s = "anagram", t = "nagaram" count = [0]×26 i=0 s='a' +1, t='n' -1 → a:1, n:-1 i=1 s='n' +1, t='a' -1 → a:0, n:0 i=2 s='a' +1, t='g' -1 → a:1, g:-1 i=3 s='g' +1, t='a' -1 → a:0, g:0 i=4 s='r' +1, t='r' -1 → r:0 i=5 s='a' +1, t='a' -1 → a:0 i=6 s='m' +1, t='m' -1 → m:0 all zero → return true
Valid Anagram
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Group Anagrams
Given an array of strings, group the anagrams together. Use a sorted version of each string as the bucket key.
Two strings are anagrams iff sorting their characters produces the same string. So use the sorted version of each string as a hash-map key and bucket the original strings under it. One pass: for each input, sort its characters, look up (or create) the bucket, append the original. Return all buckets. Sorting each string costs O(L log L); total is O(n · L log L) where L is the average length.
strs = ["eat", "tea", "tan", "ate", "nat", "bat"] key value "aet" → ["eat", "tea", "ate"] "ant" → ["tan", "nat"] "abt" → ["bat"] answer: [["eat","tea","ate"], ["tan","nat"], ["bat"]]
Group Anagrams
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Valid Parentheses
Given a string of '()[]{}', determine if the input has properly matched and nested brackets.
Use a stack. On every opening bracket, push it onto the stack. On every closing bracket, pop the top of the stack: if it doesn't match the expected opener for that closer (or the stack is empty), the string is invalid. After the whole pass, the stack must also be empty — leftover openers mean unclosed brackets.
s = "()[]{}"
c='(' push stack: [(]
c=')' pop '(' match stack: []
c='[' push stack: [[]
c=']' pop '[' match stack: []
c='{' push stack: [{]
c='}' pop '{' match stack: []
stack empty at end → validValid Parentheses
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Valid Palindrome
Given a string s, return true if it is a palindrome considering only alphanumeric characters and ignoring case.
Two pointers, l starting at 0 and r at the end. Advance each one inward, skipping any character that isn't a letter or digit. When both land on alphanumeric characters, compare them case-insensitively — if they differ, return false. Otherwise step both inward and repeat until they meet.
s = "A man, a plan, a canal: Panama" skip non-alnum, lowercase compare: l='a' vs r='a' ✓ l='m' vs r='m' ✓ l='a' vs r='a' ✓ l='n' vs r='n' ✓ l='a' vs r='a' ✓ l='p' vs r='p' ✓ ... eventually pointers meet in the middle return true
Valid Palindrome
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Longest Palindromic Substring
Given a string s, return the longest palindromic substring. Expand around each center (odd and even).
Every palindromic substring has a center. There are 2n - 1 candidate centers: n single characters (odd-length palindromes) and n - 1 pairs of adjacent characters (even-length palindromes). For each center, expand outward as long as the characters on both sides match — this finds the longest palindrome centered there in O(n). Track the best across all centers.
s = "babad" center i=0 (odd): "b" → length 1 center i=0/1 (even): "ba" no center i=1 (odd): "a" → expand "bab" length 3 ★ center i=1/2 (even): "ab" no center i=2 (odd): "b" → expand "aba" length 3 center i=2/3 (even): "ba" no center i=3 (odd): "a" → length 1 ... etc. answer: "bab" (or "aba")
Longest Palindromic Substring
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Palindromic Substrings
Given a string s, return the number of palindromic substrings in it. Expand around centers and count.
Same expand-around-center idea as Longest Palindromic Substring, but instead of tracking the longest, count every successful expansion. From each of the 2n - 1 candidate centers, every step outward where the two characters match adds one more palindrome to the count.
s = "aaa" center 0: "a" → +1 center 0/1: "aa" → +1, expand fails center 1: "a" → +1, then "aaa" → +1 center 1/2: "aa" → +1, expand fails center 2: "a" → +1 total: 6
Palindromic Substrings
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Encode and Decode Strings
Design an algorithm to encode a list of strings to one string and decode it back. Length-prefix each string with a delimiter, e.g. '5#hello'.
Encode each string as 'length#string'. The length tells the decoder exactly how many characters to read for that string — so the strings themselves can contain any character, including '#', without ambiguity. Decoding: scan forward to find the next '#', parse the number before it as the length, then take exactly that many characters as the next string. Repeat until you've consumed the input.
encode(["lint", "code", "love", "you"]) = "4#lint" + "4#code" + "4#love" + "3#you" = "4#lint4#code4#love3#you" decode: i=0 scan to '#' at 1, len=4. take s[2..6]="lint", i=6 i=6 scan to '#' at 7, len=4. take s[8..12]="code", i=12 i=12 scan to '#' at 13, len=4. take s[14..18]="love", i=18 i=18 scan to '#' at 19, len=3. take s[20..23]="you", i=23 answer: ["lint", "code", "love", "you"]
Encode and Decode Strings
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Maximum Depth of Binary Tree
Given the root of a binary tree, return its maximum depth.
An empty tree has depth 0. Otherwise the depth of the tree rooted at this node is 1 (for itself) plus the maximum depth of its two subtrees. That's the entire algorithm — the recursion does the work.
3
/ \
9 20
/ \
15 7
depth(3) = 1 + max(depth(9), depth(20))
depth(9) = 1 + max(0, 0) = 1
depth(20) = 1 + max(depth(15), depth(7)) = 1 + max(1, 1) = 2
depth(3) = 1 + max(1, 2) = 3Maximum Depth of Binary Tree
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Same Tree
Given the roots of two binary trees p and q, return true if they are structurally identical and the nodes have the same values.
Two trees are the same iff: both are null (base case true), OR both are non-null AND have the same value AND their left subtrees are the same AND their right subtrees are the same. Recurse on both pairs simultaneously.
p: q: 1 1 / \ / \ 2 3 2 3 isSame(p, q): both non-null, vals 1 == 1 isSame(p.left, q.left): both vals 2 ✓ isSame(p.right, q.right): both vals 3 ✓ → return true
Same Tree
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Invert Binary Tree
Given the root of a binary tree, invert the tree (mirror it) and return its root.
Recursively invert the left and right subtrees first, then swap them at the current node. This effectively mirrors the entire tree across its vertical axis. Equivalently: just swap children at every node — the order doesn't matter as long as each node ends up with its children flipped.
input: output:
4 4
/ \ / \
2 7 7 2
/ \ / \ / \ / \
1 3 6 9 9 6 3 1
at root 4: swap children
left subtree (recursively inverted) becomes new right
right subtree (recursively inverted) becomes new leftInvert Binary Tree
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Binary Tree Maximum Path Sum
A path is any sequence of nodes connected by edges. Return the maximum sum of any non-empty path. Track best at each node; return 'gain' upward.
Two values matter at each node. (a) The best path sum that PASSES THROUGH this node and uses BOTH children — this might be the global answer, but it can't propagate upward, since a path can only enter a parent from one side. (b) The best path sum a parent can extend through this node — uses at most ONE child (whichever has the larger gain). Recurse to get both children's gainable sums, clamp negatives to 0 (a negative gain is worse than not extending), update the global best with node.val + leftGain + rightGain, and return node.val + max(leftGain, rightGain) for the parent to consume.
-10
/ \
9 20
/ \
15 7
dfs(15) → return 15. best = max(best, 15) = 15
dfs(7) → return 7. best = max(best, 7) = 15
dfs(20) → l=15, r=7
best = max(best, 20+15+7=42) = 42 ★
return 20 + max(15, 7) = 35
dfs(9) → return 9. best = max(best, 9) = 42
dfs(-10)→ l=9, r=35
best = max(best, -10+9+35=34) = 42
return -10 + max(9, 35) = 25
answer: 42 (path 15 → 20 → 7)Binary Tree Maximum Path Sum
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Binary Tree Level Order Traversal
Given the root of a binary tree, return the level order traversal of its nodes' values (BFS).
Standard BFS with a queue, but to know where each level ends, snapshot the queue size at the start of every level — that's exactly the count of nodes belonging to this level. Pop exactly that many, collect their values into one list, and enqueue all their children for the next level. Repeat until the queue is empty.
3
/ \
9 20
/ \
15 7
queue=[3] → level: [3] enqueue 9, 20
queue=[9, 20] → level: [9, 20] enqueue 15, 7
queue=[15, 7] → level: [15, 7]
queue=[]
answer: [[3], [9, 20], [15, 7]]Binary Tree Level Order Traversal
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Serialize and Deserialize Binary Tree
Design an algorithm to serialize a binary tree to a string and deserialize it back. Preorder DFS with '#' for null works well.
Serialize via preorder DFS, emitting each node's value (and a sentinel like '#' for nulls) separated by commas. The null markers are crucial — they tell the deserializer where each subtree ends. Deserialize by tokenising the string and recursively rebuilding: read the next token; if it's '#' return null, otherwise create a node and recurse to fill in its left then right children.
tree: 1
/ \
2 3
/ \
4 5
serialize (preorder): "1,2,#,#,3,4,#,#,5,#,#"
deserialize, tokens: [1,2,#,#,3,4,#,#,5,#,#]
build(): take 1 → node(1)
left = build(): take 2 → node(2)
left = build(): take # → null
right = build(): take # → null
right = build(): take 3 → node(3)
left = build(): take 4 → node(4) with two null kids
right = build(): take 5 → node(5) with two null kids
reconstructed identical treeSerialize and Deserialize Binary Tree
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Subtree of Another Tree
Given roots root and subRoot, return true if subRoot has the same structure & values as some subtree of root.
At every node of the main tree, ask 'is this node the root of a subtree identical to subRoot?' using a standard same-tree comparison. If yes, return true. Otherwise recurse into the left and right children. The same-tree check is O(size of sub) and you do it at each of the O(n) main nodes — O(n · m) overall.
root: sub:
3 4
/ \ / \
4 5 1 2
/ \
1 2
isSubtree(3, sub):
same(3, sub)? 3 != 4, no
recurse left = isSubtree(4, sub):
same(4, sub)? structures and values match → return true
return trueSubtree of Another Tree
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Construct Tree from Preorder & Inorder
Given preorder and inorder traversals of a binary tree (with unique values), reconstruct and return the tree. preorder[0] is the root; locate it in inorder to split left/right.
Preorder visits the root before its subtrees, so preorder[0] is the root. In inorder, the root's position splits the array into the left subtree (everything before) and the right subtree (everything after). Recurse: build the left subtree from the left part of inorder, advancing through preorder; then build the right subtree from the right part. Pre-build a HashMap of value → inorderIndex so each split is O(1) instead of O(n) — that drops total time from O(n²) to O(n).
preorder = [3, 9, 20, 15, 7]
inorder = [9, 3, 15, 20, 7]
idxMap: 9→0, 3→1, 15→2, 20→3, 7→4
build(0, 4): preorder[p=0]=3 → root(3); inorder split at index 1
left = build(0, 0): preorder[p=1]=9 → leaf 9
right = build(2, 4): preorder[p=2]=20 → root(20); split at 3
left = build(2, 2): preorder[p=3]=15 → leaf 15
right = build(4, 4): preorder[p=4]=7 → leaf 7
reconstructed tree matches.Construct Tree from Preorder & Inorder
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Validate Binary Search Tree
Given the root of a binary tree, determine if it is a valid BST. Pass an open (lo, hi) range down recursively.
The naive check — 'for each node, is its left child smaller and right child larger?' — is wrong, because a node deep in the left subtree can still be larger than an ancestor higher up and the local check would miss it. The correct approach is to track an open range (lo, hi) that every node's value must fall strictly inside. Start the recursion at the root with the range (-∞, +∞). When you recurse into the left child, the new range is (lo, node.val) — the left subtree must be smaller than the current node. When you recurse right, it's (node.val, hi). If any node ever falls outside its inherited range, the tree is not a valid BST.
valid example:
5 range (-∞, +∞) ✓
/ \
3 7 (-∞, 5) ✓ (5, +∞) ✓
/ \ \
1 4 8 1 ∈ (-∞, 3) ✓
4 ∈ (3, 5) ✓
8 ∈ (7, +∞) ✓
counter-example (why local checks fail):
5
/ \
3 7
/ \
4 8 4 ∈ (5, 7)? NO — 4 ≤ 5 → INVALID
(child-only check sees 4 < 7 and incorrectly passes)Validate Binary Search Tree
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Kth Smallest Element in a BST
Given the root of a BST and an integer k, return the kth smallest value (1-indexed). Iterative inorder traversal.
An inorder traversal of a BST visits values in sorted order, so the kth value visited is the answer. Do it iteratively with an explicit stack: walk left as far as possible, pushing nodes; then pop the top, decrement k, and if k hits zero return that node's value; otherwise move into its right subtree and continue. Stops as soon as you've found it — average case faster than visiting all nodes.
3
/ \
1 4
\
2
stack = [], curr = 3, k = 2
push 3, curr = 1
push 1, curr = null
pop 1, --k = 1. not 0. curr = 1.right = 2
push 2, curr = null
pop 2, --k = 0. return 2 ✓Kth Smallest Element in a BST
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Lowest Common Ancestor of BST
Given a BST and two nodes p and q, return their lowest common ancestor. Walk down: when p and q split, that node is the LCA.
Walk down from the root. At each node, compare its value to both p.val and q.val. If both targets are smaller, the LCA is in the left subtree — descend left. If both are larger, descend right. The first time the values 'split' (one ≤ this node and the other ≥, or one equals it), this node IS the LCA — return it. O(h) without any extra space.
6
/ \
2 8
/ \ / \
0 4 7 9
/ \
3 5
p = 2, q = 8
at root 6: p.val=2 < 6, q.val=8 > 6 → split here, return 6
answer: 6Lowest Common Ancestor of BST
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Implement Trie (Prefix Tree)
Implement a Trie with insert(word), search(word), and startsWith(prefix) operations.
Each node has up to 26 children (one per lowercase letter) and a boolean 'end' flag. insert: walk character by character, creating missing children as you go; mark the final node as end. search: walk through; if any expected child is missing return false; at the end, return whether the final node has end == true. startsWith is the same walk but it ignores the end flag — just needing the path to exist is enough. All operations are O(L) where L is the word/prefix length.
insert("apple"):
root → a → p → p → l → e (mark end)
insert("app"):
walk root → a → p → p (already exist), mark this 'p' as end
search("apple"): walk a → p → p → l → e, end=true → true
search("app"): walk a → p → p, end=true → true
search("appl"): walk a → p → p → l, end=false → false
startsWith("app"): walk a → p → p exists → trueImplement Trie (Prefix Tree)
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Add and Search Word
Design WordDictionary supporting addWord and search where '.' matches any letter. DFS over a Trie when '.' is encountered.
Same Trie structure as the basic Implement Trie. addWord is identical. The twist is search: when the search character is '.', it matches any letter, so you have to try EVERY existing child at that node and recurse — return true if any branch succeeds. For a normal letter, just descend that one branch as usual. The wildcard branching is what forces a recursive (DFS) search instead of a simple walk.
add "bad", "dad", "mad" → trie has three branches under root: b, d, m
search(".ad"):
at root, '.' → try every child
branch b: search "ad" → b → a → d, end=true → true ✓
return true
search("b.."):
at root, 'b' → only branch b
in b: '.' → try every child (only 'a')
in a: '.' → try every child (only 'd')
in d: end=true → true ✓
return trueAdd and Search Word
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Word Search II
Given a board of letters and a list of words, return all words that can be formed in the board. Build a Trie of the words and DFS once.
Naively running Word Search once per query word is O(words × m·n·4^L). Build a Trie containing all the words, then DFS once from each cell, walking the Trie in lockstep — at each step, the current Trie node tells you which letters are even worth trying next. When you reach a Trie node that marks the end of a word, record it (and clear the marker so the same word isn't recorded twice). Standard backtracking on the board: replace the current cell with '#' before recursing, restore it after.
board: words = ["oath", "pea", "eat", "rain"]
o a a n
e t a e
i h k r
i f l v
build trie of all four words.
dfs from each cell:
start (0,0)='o': trie has 'o' branch → enter, mark cell '#'
move to (1,0)='e': no 'e' under o. backtrack.
move to (0,1)='a': trie has 'a' under o → enter
move to (1,1)='t': trie has 't' under oa → enter
move to (2,1)='h': trie has 'h' under oat AND end-of-word "oath"
→ add "oath" to result, clear marker
... similar walk eventually finds "eat"
answer: ["oath", "eat"]Word Search II
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Top K Frequent Elements
Given nums and integer k, return the k most frequent elements. Bucket sort by frequency for O(n).
First, count how many times each number appears using a hash map. The key observation: any frequency must be between 1 and n (the array length). So allocate n + 1 buckets, where bucket[f] holds every value that appears exactly f times. Walk those buckets from the highest index downward and pull values into your answer until you have k of them. This avoids both sorting (O(n log n)) and a heap (O(n log k)), giving you a flat O(n) algorithm because the bucket index IS the frequency — no comparisons needed.
nums = [1,1,1,2,2,3], k = 2 count: 1→3, 2→2, 3→1 buckets (index = frequency): [0] → [] [1] → [3] (3 appears once) [2] → [2] (2 appears twice) [3] → [1] (1 appears three times) walk from highest frequency: bucket[3]: take 1 → result = [1] bucket[2]: take 2 → result = [1, 2] ✓ have k answer: [1, 2]
Top K Frequent Elements
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.
Find Median from Data Stream
Design MedianFinder with addNum(num) and findMedian(). Maintain a max-heap (lower half) and min-heap (upper half), kept balanced.
Maintain two heaps balanced at the median: a max-heap (lo) holding the smaller half of the numbers, and a min-heap (hi) holding the larger half. Invariant: lo's size is always either equal to hi's size or one greater. To addNum: push into lo, then move lo's max into hi (this guarantees lo's max ≤ hi's min); rebalance by moving one back if hi got bigger than lo. To findMedian: if lo has more elements, the median is lo's max; otherwise it's the average of lo's max and hi's min. Each insert is O(log n); the median query is O(1).
addNum(1) push 1 into lo. shuttle to hi. lo smaller → rebalance back. lo = [1], hi = [] addNum(2) push 2 into lo. shuttle 2 to hi. balanced, no rebalance. lo = [1], hi = [2] findMedian() sizes equal → (1 + 2) / 2 = 1.5 addNum(3) push 3 into lo (lo top is 3). shuttle 3 to hi (hi has 2, 3). hi.size > lo.size → shuttle hi.min=2 back to lo. lo = [1, 2], hi = [3] findMedian() lo bigger → return lo.max = 2
Find Median from Data Stream
Try it first
Sketch the solution mentally — or actually code it on LeetCode. Then reveal and rate yourself honestly.