A curated ~25 problems across the 12 patterns that cover ~80% of interview questions. Every problem has a Java solution, a complexity tag, and one sentence on why the trick works.
Given an array nums and target t, return indices i, j with nums[i]+nums[j]=t.
num→index in a map. For each new num, ask the map if t-num was seen. Single pass.public int[] twoSum(int[] nums, int t) {
Map<Integer,Integer> seen = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
Integer j = seen.get(t - nums[i]);
if (j != null) return new int[]{j, i};
seen.put(nums[i], i);
}
throw new IllegalArgumentException();
}
Group strings that are anagrams of each other.
O(n·k log k) timeO(n·k) spacepublic List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> m = new HashMap<>();
for (String s : strs) {
char[] c = s.toCharArray();
Arrays.sort(c);
m.computeIfAbsent(new String(c), k -> new ArrayList<>()).add(s);
}
return new ArrayList<>(m.values());
}
Given heights, find two lines that with the x-axis form the largest container.
O(n) timeO(1) spacepublic int maxArea(int[] h) {
int l = 0, r = h.length - 1, best = 0;
while (l < r) {
int area = (r - l) * Math.min(h[l], h[r]);
best = Math.max(best, area);
if (h[l] < h[r]) l++; else r--;
}
return best;
}
Given an elevation map, compute how much rainwater it can trap.
O(n) timeO(1) spaceleftMax and rightMax. The water on each side is bounded by the smaller wall, so move the side with the smaller max.public int trap(int[] h) {
int l = 0, r = h.length - 1, lm = 0, rm = 0, water = 0;
while (l < r) {
if (h[l] < h[r]) {
lm = Math.max(lm, h[l]);
water += lm - h[l++];
} else {
rm = Math.max(rm, h[r]);
water += rm - h[r--];
}
}
return water;
}
Length of the longest substring with all distinct chars.
O(n) timeO(min(n,Σ))[l..r]; when a duplicate enters, jump l past the last occurrence.public int lengthOfLongestSubstring(String s) {
Map<Character,Integer> last = new HashMap<>();
int best = 0, l = 0;
for (int r = 0; r < s.length(); r++) {
char c = s.charAt(r);
if (last.containsKey(c) && last.get(c) >= l) l = last.get(c) + 1;
last.put(c, r);
best = Math.max(best, r - l + 1);
}
return best;
}
Smallest window in s containing all characters of t with multiplicity.
need and have. Expand right, contract left while window is valid.public String minWindow(String s, String t) {
int[] need = new int[128];
for (char c : t.toCharArray()) need[c]++;
int required = t.length(), l = 0, best = Integer.MAX_VALUE, bs = 0;
for (int r = 0; r < s.length(); r++) {
if (need[s.charAt(r)]-- > 0) required--;
while (required == 0) {
if (r - l + 1 < best) { best = r - l + 1; bs = l; }
if (++need[s.charAt(l++)] > 0) required++;
}
}
return best == Integer.MAX_VALUE ? "" : s.substring(bs, bs + best);
}
How many contiguous subarrays sum to k?
prefix - k before?”public int subarraySum(int[] nums, int k) {
Map<Integer,Integer> freq = new HashMap<>();
freq.put(0, 1);
int sum = 0, count = 0;
for (int n : nums) {
sum += n;
count += freq.getOrDefault(sum - k, 0);
freq.merge(sum, 1, Integer::sum);
}
return count;
}
Find a target in a rotated sorted array.
O(log n)public int search(int[] nums, int t) {
int l = 0, r = nums.length - 1;
while (l <= r) {
int m = (l + r) >>> 1;
if (nums[m] == t) return m;
if (nums[l] <= nums[m]) {
if (t >= nums[l] && t < nums[m]) r = m - 1; else l = m + 1;
} else {
if (t > nums[m] && t <= nums[r]) l = m + 1; else r = m - 1;
}
}
return -1;
}
Median of two sorted arrays in O(log(min(m,n))).
maxLeft ≤ minRight across both arrays.public double findMedianSortedArrays(int[] a, int[] b) {
if (a.length > b.length) return findMedianSortedArrays(b, a);
int m = a.length, n = b.length, half = (m + n + 1) / 2;
int lo = 0, hi = m;
while (lo <= hi) {
int i = (lo + hi) / 2, j = half - i;
int al = i == 0 ? Integer.MIN_VALUE : a[i-1];
int ar = i == m ? Integer.MAX_VALUE : a[i];
int bl = j == 0 ? Integer.MIN_VALUE : b[j-1];
int br = j == n ? Integer.MAX_VALUE : b[j];
if (al <= br && bl <= ar) {
if (((m + n) & 1) == 1) return Math.max(al, bl);
return (Math.max(al, bl) + Math.min(ar, br)) / 2.0;
} else if (al > br) hi = i - 1;
else lo = i + 1;
}
throw new IllegalStateException();
}
Find the minimum ship capacity that delivers all packages within D days.
O(n log S)[max(weights), sum(weights)]. Feasibility check is greedy.public int shipWithinDays(int[] w, int days) {
int lo = 0, hi = 0;
for (int x : w) { lo = Math.max(lo, x); hi += x; }
while (lo < hi) {
int m = (lo + hi) >>> 1;
int d = 1, sum = 0;
for (int x : w) {
if (sum + x > m) { d++; sum = x; } else sum += x;
}
if (d <= days) hi = m; else lo = m + 1;
}
return lo;
}
For each day, how many days until a warmer one?
O(n)public int[] dailyTemperatures(int[] t) {
int[] ans = new int[t.length];
Deque<Integer> stk = new ArrayDeque<>();
for (int i = 0; i < t.length; i++) {
while (!stk.isEmpty() && t[stk.peek()] < t[i]) {
int j = stk.pop();
ans[j] = i - j;
}
stk.push(i);
}
return ans;
}
Largest rectangle area in a histogram.
O(n)public int largestRectangleArea(int[] h) {
Deque<Integer> stk = new ArrayDeque<>();
int best = 0;
for (int i = 0; i <= h.length; i++) {
int cur = i == h.length ? 0 : h[i];
while (!stk.isEmpty() && h[stk.peek()] > cur) {
int top = stk.pop();
int left = stk.isEmpty() ? -1 : stk.peek();
best = Math.max(best, h[top] * (i - left - 1));
}
stk.push(i);
}
return best;
}
Return the k most frequent elements.
O(n log k)public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> freq = new HashMap<>();
for (int n : nums) freq.merge(n, 1, Integer::sum);
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
for (var e : freq.entrySet()) {
pq.offer(new int[]{e.getKey(), e.getValue()});
if (pq.size() > k) pq.poll();
}
int[] ans = new int[k];
for (int i = k - 1; i >= 0; i--) ans[i] = pq.poll()[0];
return ans;
}
Streaming median.
O(log n) per insertclass MedianFinder {
PriorityQueue<Integer> lo = new PriorityQueue<>(Comparator.reverseOrder());
PriorityQueue<Integer> hi = new PriorityQueue<>();
public void addNum(int n) {
lo.offer(n);
hi.offer(lo.poll());
if (hi.size() > lo.size()) lo.offer(hi.poll());
}
public double findMedian() {
return lo.size() > hi.size() ? lo.peek() : (lo.peek() + hi.peek()) / 2.0;
}
}
Count connected components of land in a grid.
O(rows·cols)public int numIslands(char[][] g) {
int rows = g.length, cols = g[0].length, count = 0;
int[][] dirs = {{0,1},{1,0},{0,-1},{-1,0}};
for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) {
if (g[i][j] != '1') continue;
count++;
Deque<int[]> q = new ArrayDeque<>();
q.offer(new int[]{i, j}); g[i][j] = '0';
while (!q.isEmpty()) {
int[] c = q.poll();
for (int[] d : dirs) {
int x = c[0]+d[0], y = c[1]+d[1];
if (x>=0&&y>=0&&x<rows&&y<cols&&g[x][y]=='1') {
g[x][y]='0'; q.offer(new int[]{x,y});
}
}
}
}
return count;
}
Shortest transformation sequence from begin to end, changing one letter per step using only words in the dictionary.
public int ladderLength(String begin, String end, List<String> words) {
Set<String> dict = new HashSet<>(words);
if (!dict.contains(end)) return 0;
Deque<String> q = new ArrayDeque<>();
q.offer(begin);
int steps = 1;
while (!q.isEmpty()) {
for (int sz = q.size(); sz > 0; sz--) {
char[] cur = q.poll().toCharArray();
if (new String(cur).equals(end)) return steps;
for (int i = 0; i < cur.length; i++) {
char old = cur[i];
for (char c = 'a'; c <= 'z'; c++) {
cur[i] = c;
String s = new String(cur);
if (dict.remove(s)) q.offer(s);
}
cur[i] = old;
}
}
steps++;
}
return 0;
}
Return any valid order to finish all courses given prerequisites; [] if impossible.
public int[] findOrder(int n, int[][] prereq) {
List<List<Integer>> g = new ArrayList<>();
for (int i = 0; i < n; i++) g.add(new ArrayList<>());
int[] in = new int[n];
for (int[] p : prereq) { g.get(p[1]).add(p[0]); in[p[0]]++; }
Deque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < n; i++) if (in[i] == 0) q.offer(i);
int[] order = new int[n]; int k = 0;
while (!q.isEmpty()) {
int u = q.poll();
order[k++] = u;
for (int v : g.get(u)) if (--in[v] == 0) q.offer(v);
}
return k == n ? order : new int[0];
}
Count components in an undirected graph given edges.
O((V+E)·α(V))public int countComponents(int n, int[][] edges) {
int[] p = new int[n], r = new int[n];
for (int i = 0; i < n; i++) p[i] = i;
int comps = n;
for (int[] e : edges) {
int a = find(p, e[0]), b = find(p, e[1]);
if (a == b) continue;
if (r[a] < r[b]) { int t=a; a=b; b=t; }
p[b] = a;
if (r[a] == r[b]) r[a]++;
comps--;
}
return comps;
}
int find(int[] p, int x) {
while (p[x] != x) { p[x] = p[p[x]]; x = p[x]; }
return x;
}
Robber on a circular street; max loot without robbing adjacent houses.
O(n)public int rob(int[] nums) {
if (nums.length == 1) return nums[0];
return Math.max(robLine(nums, 0, nums.length - 2),
robLine(nums, 1, nums.length - 1));
}
int robLine(int[] a, int l, int r) {
int prev = 0, cur = 0;
for (int i = l; i <= r; i++) {
int t = Math.max(cur, prev + a[i]);
prev = cur; cur = t;
}
return cur;
}
Minimum operations to transform w1 into w2 (insert/delete/replace).
dp[i][j] = edit distance between prefixes of length i, j. Match → diagonal; else 1 + min of three.public int minDistance(String w1, String w2) {
int m = w1.length(), n = w2.length();
int[][] dp = new int[m+1][n+1];
for (int i = 0; i <= m; i++) dp[i][0] = i;
for (int j = 0; j <= n; j++) dp[0][j] = j;
for (int i = 1; i <= m; i++) for (int j = 1; j <= n; j++) {
if (w1.charAt(i-1) == w2.charAt(j-1)) dp[i][j] = dp[i-1][j-1];
else dp[i][j] = 1 + Math.min(dp[i-1][j-1], Math.min(dp[i-1][j], dp[i][j-1]));
}
return dp[m][n];
}
Fewest coins to make amount amt.
dp[a] = min(dp[a-c]+1) over each coin c.public int coinChange(int[] coins, int amt) {
int[] dp = new int[amt + 1];
Arrays.fill(dp, amt + 1);
dp[0] = 0;
for (int a = 1; a <= amt; a++) for (int c : coins) {
if (c <= a) dp[a] = Math.min(dp[a], dp[a-c] + 1);
}
return dp[amt] > amt ? -1 : dp[amt];
}
Length of LIS in O(n log n).
tails[k] is the smallest tail of any increasing subseq of length k+1. Binary-search insert each new value.public int lengthOfLIS(int[] nums) {
int[] tails = new int[nums.length];
int len = 0;
for (int n : nums) {
int i = Arrays.binarySearch(tails, 0, len, n);
if (i < 0) i = -(i + 1);
tails[i] = n;
if (i == len) len++;
}
return len;
}
Generate all subsets of a unique-element array.
O(n·2ⁿ)public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
dfs(nums, 0, new ArrayList<>(), res);
return res;
}
void dfs(int[] a, int i, List<Integer> cur, List<List<Integer>> res) {
if (i == a.length) { res.add(new ArrayList<>(cur)); return; }
dfs(a, i+1, cur, res);
cur.add(a[i]);
dfs(a, i+1, cur, res);
cur.remove(cur.size()-1);
}
Place n queens on an n×n board with no attacks.
O(n!)r+c), “\” diagonals (r-c).public List<List<String>> solveNQueens(int n) {
List<List<String>> res = new ArrayList<>();
int[] q = new int[n];
boolean[] col = new boolean[n], d1 = new boolean[2*n], d2 = new boolean[2*n];
bt(0, n, q, col, d1, d2, res);
return res;
}
void bt(int r, int n, int[] q, boolean[] col, boolean[] d1, boolean[] d2, List<List<String>> res) {
if (r == n) { res.add(render(q, n)); return; }
for (int c = 0; c < n; c++) {
if (col[c] || d1[r+c] || d2[r-c+n]) continue;
q[r] = c; col[c] = d1[r+c] = d2[r-c+n] = true;
bt(r+1, n, q, col, d1, d2, res);
col[c] = d1[r+c] = d2[r-c+n] = false;
}
}
List<String> render(int[] q, int n) {
List<String> b = new ArrayList<>();
for (int r = 0; r < n; r++) {
char[] row = new char[n];
Arrays.fill(row, '.');
row[q[r]] = 'Q';
b.add(new String(row));
}
return b;
}
Find all dictionary words present on the board (with reuse rules).
O(W·L + cells·4^L)class Trie { Trie[] kids = new Trie[26]; String word; }
public List<String> findWords(char[][] board, String[] words) {
Trie root = new Trie();
for (String w : words) {
Trie n = root;
for (char c : w.toCharArray()) {
if (n.kids[c-'a'] == null) n.kids[c-'a'] = new Trie();
n = n.kids[c-'a'];
}
n.word = w;
}
List<String> res = new ArrayList<>();
for (int i = 0; i < board.length; i++)
for (int j = 0; j < board[0].length; j++)
dfs(board, i, j, root, res);
return res;
}
void dfs(char[][] b, int i, int j, Trie n, List<String> res) {
if (i<0||j<0||i>=b.length||j>=b[0].length) return;
char c = b[i][j];
if (c == '#' || n.kids[c-'a'] == null) return;
n = n.kids[c-'a'];
if (n.word != null) { res.add(n.word); n.word = null; }
b[i][j] = '#';
dfs(b, i+1, j, n, res); dfs(b, i-1, j, n, res);
dfs(b, i, j+1, n, res); dfs(b, i, j-1, n, res);
b[i][j] = c;
}