๐ Recommended Learning Sequence
Build the first window once in O(k), then slide by adding the new right element and subtracting the old left element โ O(1) per step, O(n) total.
Three-step loop: โ EXPAND right, โก SHRINK left while invalid, โข UPDATE best. Each element enters and exits at most once โ O(n).
Start with left=0, right=n-1. Move left right when sum is too small, move right left when sum is too large. Pointers converge until found or they meet.
Slow = write pointer (๐ข), fast = scan pointer (๐). When fast finds a valid element, slow writes it and advances. Duplicates/invalids are silently skipped.
Sort, then fix nums[i] and sweep L=i+1, R=n-1 inward. Sum too small โ L++. Sum too large โ R--. Match โ record triplet, skip duplicates. Reduces O(nยณ) brute-force to O(nยฒ).
Build prefix[i] = prefix[i-1] + nums[i-1] in O(n). Then any range sum nums[l..r] = prefix[r+1] - prefix[l] in O(1). Pair with HashMap to count subarrays with sum = k.
Store valueโindex in a HashMap as you scan. For each nums[i], check if complement (target - nums[i]) exists in seen โ O(1) lookup. Enables Two Sum, anagram grouping, frequency counting, consecutive sequences.
currMax = max(nums[i], currMax + nums[i]) โ at each position either extend the current subarray or restart fresh. Track global maxSoFar. One pass O(n), constant space. Handles all-negative arrays.
Maintain lo=0, hi=nโ1. Compute mid each step โ if nums[mid] < target go โ๏ธ right (lo=mid+1), if larger go โ๏ธ left (hi=midโ1). Each step โ๏ธ eliminates half the array. Found or lo>hi.
Don't search the array โ search the answer range [lo..hi]. Test each mid with isFeasible(mid) in O(n). โ feasible โ save ans, try smaller (hi=midโ1). โ not feasible โ try larger (lo=mid+1).
Push opening brackets ๐ฅ; when a closing bracket arrives, pop the top and verify it matches โ . LIFO guarantees correct nesting โ most-recently opened must close first. Empty stack on close or non-empty at end โ invalid.
Maintain indices in decreasing value order ๐. When nums[i] > nums[stack.top()], current index is the Next Greater Element for every index we pop ๐ฅ. Each element pushed & popped at most once โ O(n) total.
Maintain indices in increasing height order ๐. When height[i] < height[stack.top()], pop and compute area = height ร width ๐. Width = distance to previous smaller bar. Sentinel 0 at end flushes all remaining bars.
Enqueue start ๐ฅ, mark visited. Snapshot queue size at each outer loop โ drain one full level ๐ before advancing. First time a node is reached = shortest distance. Works on grids, trees, and graphs.
Pre-load ALL source cells into the queue ๐ด๐ด before the BFS loop. The wavefront expands from every source simultaneously โ each cell records distance to its nearest source in one pass. No re-runs needed.
๐ข slow moves 1 step, ๐ fast moves 2 steps. If there's a cycle they must meet. After meeting, reset slow to head and move both 1 step โ they reunite exactly at the cycle start.
๐พ Save next โ ๐ flip curr.next = prev โ advance prev = curr โ advance curr = next. Each arrow flips one at a time until the whole list is reversed in O(n) / O(1).
๐ Advance fast n+1 steps ahead of slow. Move both until fast hits null โ slow is exactly one node before the target. โ๏ธ Skip it. For merge: compare heads, always pick the smaller one.
One pattern, three timings โ move result.add(node.val) to change the order. ๐ฟ Pre-order: visit root first (serialize). ๐ถ In-order: between subtrees (BST sorted). ๐ฃ Post-order: root last (height, diameter).
Snapshot size = q.size() at the start of each outer loop โ that's exactly one level. Poll size nodes, enqueue their children, then store the level. Repeat until the queue is empty.
Two patterns in one: ๐ค Root-to-leaf โ add node, recurse with remโval, backtrack at return. ๐ Max path sum โ post-order bottom-up; L + R + node.val is the path through this node; return only the better arm upward.
Three operations on one property: ๐ Validate โ pass (min,max) bounds down; fail if node outside range. ๐ Search โ halve the tree each step. ๐ Build balanced โ pick sorted array midpoint as root, recurse on halves.
result.add(new ArrayList<>(curr)) at the very start of each call โ this captures every state including the empty set. Loop from start, add element, recurse with i+1, then remove (backtrack). Subsets II: sort + skip nums[i]==nums[i-1] when i>start.
boolean[] used prevents re-picking the same element. At each call: try every index where !used[i], mark used, recurse, unmark (backtrack). Collect only at leaves (curr.size()==n).
Key insight: sort candidates. If candidates[i] > remain, use break โ all subsequent candidates are larger too, so the entire rest of the loop is pruned. Collect when remain==0. For Combination Sum, recurse with i (reuse allowed).
Min Platforms: sort arrivals & departures separately; two-pointer sweep counts how many overlap at once. Merge Intervals: sort by start, extend last interval when next.start โค last.end. Sorting makes the greedy choice trivially local.
Jump I: maxReach = max(maxReach, i+nums[i]) โ if ever i > maxReach, blocked โ false. Jump II: also track currEnd (boundary of current jump level); when i == currEnd, you must jump โ jumps++; currEnd = maxReach.
Maintain a min-heap of exactly K elements. For each new number: offer(num), if size > K then poll() (drops smallest). After scanning all elements, peek() = Kth largest. New element < heap root โ gets discarded immediately.
LO (max-heap) holds the lower half, HI (min-heap) holds the upper half. Rule: lo.offer(num) โ hi.offer(lo.poll()) โ if lo.size < hi.size: lo.offer(hi.poll()). Median = lo.peek() or average of both tops.
Push head of each sorted list into a min-heap. Repeat: poll() global minimum โ append to result โ push node.next from the same list. Heap size stays โค K at all times. O(n log K) for n total nodes.
Each node starts as its own parent. find(x) returns root with path compression โ parent[x] jumps directly to root. union(x,y) merges by rank: smaller tree attaches under larger. Same root = connected.
Build inDegree[]. Seed queue with all nodes having inDegree=0. BFS: pop node โ add to order โ decrement neighbors' inDegree; if 0 โ enqueue. If order.size() < n โ cycle detected.
dist[src]=0, all others=โ. Min-heap stores (dist, node). Poll smallest: skip if stale (d > dist[u]), else relax all neighbors. โ ๏ธ Only works for non-negative weights โ use Bellman-Ford for negatives.
Run Vโ1 rounds: each round relaxes all edges. If dist[u]+w < dist[v], update. For K-stop: run K+1 rounds using a snapshot copy of prev dist to prevent chaining in one round. Negative cycle: V-th round still improves.
Start from node 0. Min-heap stores (weight, node). Pop cheapest: if already in MST โ skip (stale). Else mark visited, add cost, push all unvisited neighbors. MST always has exactly Vโ1 edges.
- State depends only on previous 1โ2 values
- Climbing stairs, Fibonacci, min cost steps
- House Robber:
dp[i] = max(dp[i-1], dp[i-2]+nums[i]) - Decode Ways: count valid decodings
// House Robber โ space optimized int prev2 = 0, prev1 = 0; for (int num : nums) { int curr = Math.max(prev1, prev2 + num); prev2 = prev1; prev1 = curr; } return prev1; // Coin Change (unbounded knapsack): int[] dp = new int[amount + 1]; Arrays.fill(dp, amount + 1); dp[0] = 0; for (int coin : coins) for (int j = coin; j <= amount; j++) dp[j] = Math.min(dp[j], dp[j - coin] + 1);
- Count/find paths in a grid
- Fill row by row, each cell from top and left
- Obstacles: set dp cell to 0
- Triangle: bottom-up
int[][] dp = new int[m][n]; dp[0][0] = 1; // Init first row and first column for (int i = 0; i < m; i++) dp[i][0] = 1; for (int j = 0; j < n; j++) dp[0][j] = 1; // Fill: paths = from left + from top for (int i = 1; i < m; i++) for (int j = 1; j < n; j++) dp[i][j] = dp[i-1][j] + dp[i][j-1]; return dp[m-1][n-1];
- LCS: if match โ
dp[i-1][j-1]+1, elsemax(dp[i-1][j], dp[i][j-1]) - Edit Distance: if match โ
dp[i-1][j-1], else1 + min(del, ins, replace) - Longest Common Substring: if match โ
dp[i-1][j-1]+1, else0
int[][] dp = new int[m+1][n+1]; for (int i=0; i<=m; i++) dp[i][0] = i; // delete all for (int j=0; j<=n; j++) dp[0][j] = j; // insert all for (int i=1; i<=m; i++) for (int j=1; j<=n; j++) { if (s1.charAt(i-1)==s2.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];
- Longest strictly increasing subsequence
- O(nยฒ): dp[i] = max(dp[j]+1) for j<i if nums[j]<nums[i]
- O(n log n): maintain tails array + binary search
List<Integer> tails = new ArrayList<>(); for (int num : nums) { int lo=0, hi=tails.size(); while (lo < hi) { // binary search int mid=(lo+hi)/2; if (tails.get(mid) < num) lo=mid+1; else hi=mid; } if (lo == tails.size()) tails.add(num); else tails.set(lo, num); // replace with smaller tail } return tails.size();
- Negative ร negative = positive (min can become max)
- Track both currMin and currMax at each index
- At each step: new min/max from (curr, currรmax, currรmin)
int maxProd=nums[0], minProd=nums[0], ans=nums[0]; for (int i=1; i<nums.length; i++) { int n=nums[i]; int newMax=Math.max(n, Math.max(maxProd*n, minProd*n)); int newMin=Math.min(n, Math.min(maxProd*n, minProd*n)); maxProd=newMax; minProd=newMin; ans=Math.max(ans, maxProd); } return ans;
a ^ a = 0(same number cancels)a ^ 0 = a(XOR with 0 is identity)- XOR all elements โ duplicates cancel โ left with unique
- Two unique numbers: split into groups by differing bit
// XOR all โ single unique number int xor = 0; for (int n : nums) xor ^= n; return xor; // pairs cancel out // Two unique numbers (#260): int xorAll = 0; for (int n : nums) xorAll ^= n; int bit = xorAll & (-xorAll); // lowest set bit int a = 0, b = 0; for (int n : nums) if ((n & bit) != 0) a ^= n; else b ^= n; // Add without + (#371): while (b != 0) { int carry = (a & b) << 1; a = a ^ b; b = carry; }
- Count bits:
n &= (n-1)removes lowest bit โ count ops - Power of 2:
n > 0 && (n & (n-1)) == 0 - Counting Bits DP:
dp[i] = dp[i>>1] + (i & 1)
// Kernighan's โ count set bits O(k) int count = 0; while (n != 0) { n &= (n - 1); count++; } // Power of 2 boolean isPow2 = n > 0 && (n & (n-1)) == 0; // Counting bits for [0..n] โ DP int[] dp = new int[n+1]; for (int i=1; i<=n; i++) dp[i] = dp[i >> 1] + (i & 1);
- Every number appears 3ร except one (appears once)
- Maintain
onesandtwosbit accumulators - After 3 occurrences, bit resets in both
- Generalizes to "appears k times except one"
int ones = 0, twos = 0; for (int n : nums) { ones = (ones ^ n) & ~twos; twos = (twos ^ n) & ~ones; } return ones; // bit remaining after mod-3
// int โ String (3 ways) String s = Integer.toString(42); String s = String.valueOf(42); String s = 42 + ""; // quick but slow // String โ int int n = Integer.parseInt("42"); int n = Integer.valueOf("42"); // returns Integer (autoboxed) // String โ long/double long l = Long.parseLong("123456789"); double d = Double.parseDouble("3.14");
// char โ 0-based alphabet index int idx = 'c' - 'a'; // 2 int idx = 'C' - 'A'; // 2 // char digit โ int value int d = '7' - '0'; // 7 int d = Character.getNumericValue('7'); // 7 // int โ char char c = (char)('a' + 2); // 'c' char c = (char)(65); // 'A' (ASCII) // char โ ASCII int int ascii = (int)'A'; // 65
// String โ char[] char[] arr = s.toCharArray(); // char[] โ String String s = new String(arr); String s = String.valueOf(arr); // int[] โ List<Integer> List<Integer> list = Arrays.stream(arr) .boxed().collect(Collectors.toList()); // List<Integer> โ int[] int[] arr = list.stream() .mapToInt(Integer::intValue).toArray(); // int โ binary/hex/octal string Integer.toBinaryString(10); // "1010" Integer.toHexString(255); // "ff" Integer.toOctalString(8); // "10"
// Sort int[] ascending Arrays.sort(arr); // Sort int[] descending โ must use Integer[] Integer[] boxed = Arrays.stream(arr).boxed() .toArray(Integer[]::new); Arrays.sort(boxed, Collections.reverseOrder()); // Or: sort ascending, then reverse manually Arrays.sort(arr); for (int l=0,r=arr.length-1; l<r; l++,r--) { int tmp=arr[l]; arr[l]=arr[r]; arr[r]=tmp; } // Reverse a List Collections.reverse(list);
Integer.MAX_VALUE // 2^31 - 1 = 2,147,483,647 Integer.MIN_VALUE // -2^31 = -2,147,483,648 Long.MAX_VALUE // 2^63 - 1 Long.MIN_VALUE // -2^63 Double.MAX_VALUE Double.MIN_VALUE // smallest positive (not most negative!) // โ OVERFLOW: Integer.MAX_VALUE + 1 wraps to MIN_VALUE! // Safe add check: boolean overflow = a > Integer.MAX_VALUE - b; // Common safe "infinity" for DP/shortest path: int INF = (int) 1e9; // 1_000_000_000 int INF = Integer.MAX_VALUE / 2; // safe for addition
// โ Wrong โ can overflow if lo+hi > MAX_VALUE int mid = (lo + hi) / 2; // โ Correct int mid = lo + (hi - lo) / 2; // โ Also correct (unsigned right shift) int mid = (lo + hi) >>> 1; // Long midpoint long mid = lo + (hi - lo) / 2;
// Init DP array with "infinity" Arrays.fill(dp, Integer.MAX_VALUE); // โ add carefully Arrays.fill(dp, (int) 1e9); // safer for addition // Init 2D DP for (int[] row : dp) Arrays.fill(row, (int) 1e9); // Negative infinity (max-finding DP) Arrays.fill(dp, Integer.MIN_VALUE); Arrays.fill(dp, (int) -1e9);
Math.max(a, b) // larger of two Math.min(a, b) // smaller of two Math.abs(x) // absolute value Math.pow(base, exp) // returns double! cast if needed Math.sqrt(n) // returns double (int) Math.sqrt(n) // integer square root // Check if n is perfect square: int sq = (int) Math.sqrt(n); boolean isPerfect = sq * sq == n; // Ceiling division (without float): int ceil = (a + b - 1) / b; // ceil(a/b) for positive b int ceil = (int) Math.ceil((double) a / b);
Math.floor(d) // rounds toward -โ Math.ceil(d) // rounds toward +โ Math.round(d) // rounds to nearest (ties โ up) Math.log(n) // natural log (base e) Math.log(n) / Math.log(2) // log base 2 Math.log10(n) // log base 10 // Number of digits in n (n > 0): (int) Math.log10(n) + 1 // GCD using Math (Java 5+, not in older contest): // Implement manually (see DSA Blocks section) Math.PI // 3.14159... Math.E // 2.71828...
Character.isDigit(c) // '0'..'9' Character.isLetter(c) // a-z, A-Z (and Unicode) Character.isLetterOrDigit(c) // alphanumeric Character.isAlphabetic(c) // broader than isLetter Character.isWhitespace(c) // space, tab, newline Character.isUpperCase(c) // A-Z Character.isLowerCase(c) // a-z // Fast inline check (ASCII only): c >= 'a' && c <= 'z' // lowercase letter c >= 'A' && c <= 'Z' // uppercase letter c >= '0' && c <= '9' // digit
Character.toUpperCase(c) // 'a' โ 'A' Character.toLowerCase(c) // 'A' โ 'a' // Bit-level case tricks (ASCII letters only): c | ' ' // force lowercase ('A'โ'a', 'a'โ'a') c & '_' // force uppercase ('a'โ'A', 'A'โ'A') c ^ ' ' // toggle case ('a'โ'A') // (' ' = 32 = 0b0010_0000, '_' = 95 = 0b0101_1111) // Vowel check: "aeiouAEIOU".indexOf(c) != -1 "aeiou".indexOf(Character.toLowerCase(c)) != -1
s.length() s.charAt(i) // char at index i s.substring(i) // [i, end) s.substring(i, j) // [i, j) โ j exclusive s.indexOf("sub") // first occurrence, -1 if not found s.indexOf("sub", fromIdx) // search starting at fromIdx s.lastIndexOf("sub") // last occurrence s.contains("sub") // boolean s.startsWith("pre") // boolean s.endsWith("suf") // boolean s.isEmpty() // length == 0 s.isBlank() // all whitespace (Java 11+)
s.toLowerCase() s.toUpperCase() s.trim() // remove leading/trailing ASCII whitespace s.strip() // Unicode-aware trim (Java 11+) s.replace('a', 'b') // all char replacements s.replace("old", "new") // all substring replacements s.replaceAll("[^a-z]", "") // regex remove non-alpha s.split(" ") // split by space s.split("\\s+") // split by any whitespace s.split(",", -1) // -1 keeps trailing empty strings String.join("-", list) // join with delimiter "ab".repeat(3) // "ababab" (Java 11+)
s.equals(other) // โ structural equality s == other // โ reference equality (don't use) s.equalsIgnoreCase(other) s.compareTo(other) // lexicographic: neg/0/pos s.compareToIgnoreCase(other) // Sort by canonical form (anagram grouping): char[] arr = s.toCharArray(); Arrays.sort(arr); String key = new String(arr); // sorted key // Sorted string == sorted other โ anagrams // Freq array as key (no sort needed): int[] freq = new int[26]; for (char c : s.toCharArray()) freq[c-'a']++; Arrays.toString(freq); // use as HashMap key
StringBuilder sb = new StringBuilder(); sb.append("text"); sb.append(c); // append char sb.append(n); // append int sb.insert(idx, "text"); // insert at position sb.delete(i, j); // delete [i, j) sb.deleteCharAt(i); sb.replace(i, j, "new"); // replace range sb.reverse(); // in-place reverse โ very useful! sb.charAt(i); sb.setCharAt(i, c); sb.indexOf("text"); sb.length(); sb.toString(); // convert to String // Chaining: new StringBuilder(s).reverse().toString(); // reverse string
Arrays.sort(arr) // ascending, O(n log n) Arrays.sort(arr, from, to) // sort range [from, to) Arrays.fill(arr, val) // fill entire array Arrays.fill(arr, from, to, val) // fill range // 2D array fill: for (int[] row : grid) Arrays.fill(row, -1); // Sort 2D array by first column: Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
Arrays.copyOf(arr, newLen) // copy, pad/trim to newLen Arrays.copyOfRange(arr, from, to) // copy [from, to) Arrays.equals(arr1, arr2) // element-wise equality Arrays.deepEquals(a, b) // for 2D arrays Arrays.toString(arr) // "[1, 2, 3]" โ debug print Arrays.deepToString(grid) // 2D print // Clone array: int[] copy = arr.clone(); int[] copy = Arrays.copyOf(arr, arr.length);
// Binary search (array MUST be sorted!): int idx = Arrays.binarySearch(arr, key); // Returns: index if found, else -(insertionPoint)-1 // Insertion point: if not found, idx = -(insertionPoint)-1 // So insertionPoint = -(idx+1) // Stream operations: Arrays.stream(arr).sum() Arrays.stream(arr).min().getAsInt() Arrays.stream(arr).max().getAsInt() Arrays.stream(arr).average().getAsDouble() Arrays.stream(arr).distinct().toArray() // List from array (fixed-size!): List<Integer> list = Arrays.asList(1, 2, 3);
Collections.sort(list) // natural order Collections.sort(list, comparator) // custom order Collections.reverse(list) // reverse in-place Collections.shuffle(list) // random shuffle Collections.swap(list, i, j) // swap two elements Collections.rotate(list, dist) // rotate right by dist // Reverse comparator (for sort descending): Collections.reverseOrder() // Comparator list.sort(Collections.reverseOrder());
Collections.min(list) Collections.max(list) Collections.min(list, comparator) Collections.max(list, comparator) Collections.frequency(list, element) // count occurrences Collections.disjoint(list1, list2) // true if no common elements Collections.nCopies(n, val) // immutable list of n copies
Collections.emptyList() // immutable empty list Collections.emptyMap() Collections.emptySet() Collections.singletonList(val) // immutable 1-element list Collections.unmodifiableList(list) // read-only view // Mutable list from array values: List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3)); // List.of (Java 9+, immutable): List<Integer> list = List.of(1, 2, 3); // Set.of / Map.of (Java 9+, immutable): Set<Integer> s = Set.of(1, 2, 3);
map.put(key, val) map.get(key) // null if missing map.getOrDefault(key, 0) // โ most common pattern map.containsKey(key) map.containsValue(val) map.remove(key) map.size() map.isEmpty() map.putIfAbsent(key, val) // only if key not present map.computeIfAbsent(key, k -> new ArrayList<>()) map.getOrDefault(key, defVal) map.entrySet() // Set<Entry<K,V>> map.keySet() // Set<K> map.values() // Collection<V>
// Option 1: getOrDefault + put map.put(key, map.getOrDefault(key, 0) + 1); // Option 2: merge (cleanest) map.merge(key, 1, Integer::sum); // Option 3: computeIfAbsent for grouped lists map.computeIfAbsent(key, k -> new ArrayList<>()).add(val); // Iterate and process for (Map.Entry<String, Integer> e : map.entrySet()) { String k = e.getKey(); int v = e.getValue(); } // Decrement and remove if zero: map.merge(key, -1, Integer::sum); if (map.get(key) == 0) map.remove(key);
Set<Integer> set = new HashSet<>(); set.add(val) set.contains(val) // O(1) set.remove(val) set.size() // Set from array: Set<Integer> set = new HashSet<>(Arrays.asList(arr)); // Intersection: set1.retainAll(set2); // Union: set1.addAll(set2); // Difference: set1.removeAll(set2); // Visited pattern (BFS/DFS): Set<Integer> visited = new HashSet<>(); if (!visited.contains(node)) { visited.add(node); ... }
// Min-heap (default) โ smallest element at top PriorityQueue<Integer> minPQ = new PriorityQueue<>(); // Max-heap โ largest element at top PriorityQueue<Integer> maxPQ = new PriorityQueue<>( Collections.reverseOrder()); // OR: PriorityQueue<Integer> maxPQ = new PriorityQueue<>((a,b) -> b-a); // PQ operations: pq.offer(val) // add (returns false if capacity exceeded) pq.add(val) // add (throws exception) pq.poll() // remove & return top (null if empty) pq.peek() // view top without removing pq.size() pq.isEmpty()
// Sort int[] by second element: new PriorityQueue<>((a,b) -> a[1] - b[1]); // Sort by frequency (ascending): new PriorityQueue<>((a,b) -> freq.get(a) - freq.get(b)); // Sort by frequency desc, then value asc: new PriorityQueue<>((a,b) -> { if (!freq.get(a).equals(freq.get(b))) return freq.get(b) - freq.get(a); // desc freq return a - b; // asc value }); // Comparator.comparing chain: Comparator.comparingInt((int[] a) -> a[0]) .thenComparingInt(a -> a[1]); // โ Use Integer subtraction only when no overflow risk! // Safe: Comparator.comparingInt(x -> x)
// Sort intervals by start time: Arrays.sort(intervals, (a,b) -> a[0] - b[0]); // Sort intervals by end time: Arrays.sort(intervals, (a,b) -> a[1] - b[1]); // Sort strings by length: Arrays.sort(strs, (a,b) -> a.length() - b.length()); Arrays.sort(strs, Comparator.comparingInt(String::length)); // Sort by length then alphabetically: Arrays.sort(strs, Comparator.comparingInt(String::length) .thenComparing(Comparator.naturalOrder())); // Largest Number (#179) โ custom concat comparison: Arrays.sort(strs, (a,b) -> (b+a).compareTo(a+b)); // Sort 2D by start asc, end desc: Arrays.sort(arr, (a,b) -> a[0]!=b[0] ? a[0]-b[0] : b[1]-a[1]);
Deque<Integer> stack = new ArrayDeque<>(); stack.push(x) // push to top = addFirst stack.pop() // pop from top = removeFirst (throws if empty) stack.peek() // peek top = peekFirst (null if empty) stack.isEmpty() // โ Prefer ArrayDeque over Stack class // Stack is synchronized (slow); ArrayDeque is not
Deque<Integer> queue = new ArrayDeque<>(); // OR: Queue<Integer> queue = new LinkedList<>(); queue.offer(x) // enqueue (addLast) โ returns false if full queue.add(x) // enqueue โ throws if full queue.poll() // dequeue front โ null if empty queue.remove() // dequeue front โ throws if empty queue.peek() // view front โ null if empty queue.isEmpty()
Deque<Integer> dq = new ArrayDeque<>(); dq.offerFirst(x) // add to front dq.offerLast(x) // add to back dq.pollFirst() // remove from front dq.pollLast() // remove from back dq.peekFirst() // view front dq.peekLast() // view back // Sliding window maximum (monotonic decreasing deque): // Store indices; front = max of window while (!dq.isEmpty() && nums[dq.peekLast()] < nums[i]) dq.pollLast(); // remove smaller from back dq.offerLast(i); if (dq.peekFirst() <= i - k) dq.pollFirst(); // out of window
// Check if bit i is set (0-indexed from right): (n >> i) & 1 // 1 if set, 0 if not (n & (1 << i)) != 0 // boolean check // Set bit i (force to 1): n |= (1 << i); // Clear bit i (force to 0): n &= ~(1 << i); // Toggle bit i (flip): n ^= (1 << i); // Update bit i to value v (0 or 1): n = (n & ~(1 << i)) | (v << i);
// Get lowest set bit (isolate rightmost 1): n & (-n) // e.g. 12(1100) โ 4(0100) n & (~n + 1) // same (two's complement) // Clear lowest set bit (Kernighan's trick): n &= (n - 1); // removes one '1' per iteration // Count set bits โ Kernighan's O(k): int count = 0; while (n != 0) { n &= (n-1); count++; } // Count set bits โ Java built-in O(1): Integer.bitCount(n); // Check power of 2: n > 0 && (n & (n-1)) == 0 // Highest one bit (floor power of 2): Integer.highestOneBit(n);
// Multiply / divide by power of 2: n << k // n ร 2^k n >> k // n รท 2^k (arithmetic โ preserves sign) n >>> k // n รท 2^k (logical โ fills with 0) // Even / odd: (n & 1) == 0 // even (n & 1) == 1 // odd // XOR swap (no temp variable): a ^= b; b ^= a; a ^= b; // Sign of integer: n >> 31 // -1 if negative, 0 if non-negative // Negate using bits (two's complement): ~n + 1 // same as -n // Midpoint (no overflow): (lo + hi) >>> 1
// XOR identity rules: a ^ 0 == a // unchanged a ^ a == 0 // self-cancels โ key for Single Number a ^ b ^ a == b // associative + self-cancel // Bitmask for subset of n bits: (1 << n) - 1 // e.g. n=4 โ 0b1111 = 15 // Enumerate all subsets of bitmask m: for (int sub = m; sub > 0; sub = (sub-1) & m) { ... } // All bits set for n-bit number: (1 << n) - 1 // Check if exactly one bit set (power of 2): Integer.bitCount(n) == 1 // Number of trailing zeros: Integer.numberOfTrailingZeros(n) Integer.numberOfLeadingZeros(n)
// 4-directional (up, right, down, left): int[] dr = {-1, 0, 1, 0}; int[] dc = { 0, 1, 0, -1}; // Or as a single array (pairs: dr[d], dr[d+1]): int[] dirs = {-1,0, 1,0, 0,-1, 0,1}; // 8-directional (includes diagonals): int[] dr = {-1,-1,-1, 0, 0, 1, 1, 1}; int[] dc = {-1, 0, 1,-1, 1,-1, 0, 1}; // Usage: for (int d = 0; d < 4; d++) { int nr = r + dr[d], nc = c + dc[d]; if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) // valid neighbor }
// 2D โ 1D index: int idx = r * cols + c; // 1D โ 2D index: int r = idx / cols; int c = idx % cols; // Transpose (swap [i][j] and [j][i]): for (int i = 0; i < n; i++) for (int j = i+1; j < n; j++) { int tmp = mat[i][j]; mat[i][j] = mat[j][i]; mat[j][i] = tmp; } // Rotate 90ยฐ clockwise: transpose โ reverse each row // Rotate 90ยฐ counter-clockwise: transpose โ reverse each col // Rotate 180ยฐ: reverse each row โ reverse each col
// Spiral traversal: 4 boundaries shrinking int top=0, bot=m-1, left=0, right=n-1; while (top <= bot && left <= right) { // traverse top row, right col, bottom row, left col top++; right--; bot--; left++; } // Diagonal traversal: // Main diagonal: matrix[i][i] // Anti-diagonal: matrix[i][n-1-i] // All same diagonal: (r - c) is constant // All same anti-diagonal: (r + c) is constant // DFS on grid (4-dir): void dfs(int[][] g, boolean[][] vis, int r, int c) { if (r<0||r>=g.length||c<0||c>=g[0].length||vis[r][c]) return; vis[r][c] = true; dfs(g,vis,r-1,c); dfs(g,vis,r+1,c); dfs(g,vis,r,c-1); dfs(g,vis,r,c+1); }
// GCD โ Euclidean O(log min(a,b)) int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } // LCM (use long to avoid overflow) long lcm(long a, long b) { return a / gcd((int)a, (int)b) * b; } // Is prime โ O(โn) boolean isPrime(int n) { if (n < 2) return false; for (int i = 2; (long)i*i <= n; i++) if (n % i == 0) return false; return true; } // Count digits in n (n > 0): (int) Math.log10(n) + 1 String.valueOf(n).length() // simpler
// Returns index of smallest element (rotation point) int findPivot(int[] nums) { int lo = 0, hi = nums.length - 1; while (lo < hi) { int mid = lo + (hi - lo) / 2; if (nums[mid] > nums[hi]) lo = mid + 1; else hi = mid; } return lo; // index of minimum } // After finding pivot p: // Left sorted portion: [0, p-1] // Right sorted portion: [p, n-1] // Standard binary search on the correct half
// String palindrome (two pointers): boolean isPalin(String s) { int l = 0, r = s.length() - 1; while (l < r) if (s.charAt(l++) != s.charAt(r--)) return false; return true; } // Integer palindrome (no string): boolean isPalinNum(int x) { if (x < 0 || (x % 10 == 0 && x != 0)) return false; int rev = 0; while (x > rev) { rev = rev*10 + x%10; x /= 10; } return x == rev || x == rev/10; } // Expand around center (longest palindrome): int expand(String s, int l, int r) { while (l>=0 && r<s.length() && s.charAt(l)==s.charAt(r)) { l--; r++; } return r - l - 1; // length }
// ListNode (Linked List) static class ListNode { int val; ListNode next; ListNode(int v) { val = v; } } // TreeNode (Binary Tree) static class TreeNode { int val; TreeNode left, right; TreeNode(int v) { val = v; } } // TrieNode static class TrieNode { TrieNode[] children = new TrieNode[26]; boolean isEnd; } // Graph node (Clone Graph) static class Node { int val; List<Node> neighbors = new ArrayList<>(); Node(int v) { val = v; } }
final int MOD = (int) 1e9 + 7; // common modulus // Modular exponentiation O(log exp): long modPow(long base, long exp, long mod) { long result = 1; base %= mod; while (exp > 0) { if ((exp & 1) == 1) result = result * base % mod; base = base * base % mod; exp >>= 1; } return result; } // Safe modular addition: (int)(((long) a + b) % MOD) // Safe modular multiplication: (int)((long) a * b % MOD)
int[] parent, rank; void init(int n) { parent = new int[n]; rank = new int[n]; for (int i = 0; i < n; i++) parent[i] = i; } int find(int x) { // path compression return parent[x] == x ? x : (parent[x] = find(parent[x])); } boolean union(int x, int y) { // union by rank int px = find(x), py = find(y); if (px == py) return false; if (rank[px] < rank[py]) { int t=px; px=py; py=t; } parent[py] = px; if (rank[px] == rank[py]) rank[px]++; return true; }
// By length (ascending): Arrays.sort(arr, (a,b) -> a.length() - b.length()); // By length descending: Arrays.sort(arr, (a,b) -> b.length() - a.length()); // By length, then alphabetically (tie-break): Arrays.sort(arr, Comparator.comparingInt(String::length) .thenComparing(Comparator.naturalOrder())); // Largest number concatenation (#179): Arrays.sort(arr, (a,b) -> (b+a).compareTo(a+b)); // Case-insensitive alphabetical: Arrays.sort(arr, String.CASE_INSENSITIVE_ORDER);
// By first column ascending: Arrays.sort(arr, (a,b) -> a[0] - b[0]); // By end time, then start time desc: Arrays.sort(arr, (a,b) -> a[1] != b[1] ? a[1] - b[1] : b[0] - a[0]); // Sort List<int[]>: list.sort((a,b) -> a[0] - b[0]); // Sort by absolute value: Arrays.sort(arr, (a,b) -> Math.abs(a) - Math.abs(b)); // Reverse natural order: Arrays.sort(arr, Collections.reverseOrder()); // Integer[] only list.sort(Comparator.reverseOrder());
// โ OVERFLOW: use only when values safely fit in int (a, b) -> a - b // wrong if a=-2B, b=+2B // โ Safe alternatives: Integer.compare(a, b) // returns -1, 0, or 1 Comparator.comparingInt(x -> x) (a, b) -> a < b ? -1 : (a > b ? 1 : 0) // โ Wrong for negative numbers mod: a % n // Java: -7 % 3 = -1, not 2 // โ Always positive modulo: ((a % n) + n) % n
TreeMap<Integer,Integer> map = new TreeMap<>(); map.firstKey() // smallest key map.lastKey() // largest key map.floorKey(k) // greatest key โค k (null if none) map.ceilingKey(k) // smallest key โฅ k (null if none) map.lowerKey(k) // greatest key < k map.higherKey(k) // smallest key > k map.headMap(k) // view of keys < k map.tailMap(k) // view of keys โฅ k map.subMap(lo, hi) // view of keys in [lo, hi) map.pollFirstEntry() // remove & return smallest map.pollLastEntry() // remove & return largest // Descending order: new TreeMap<>(Collections.reverseOrder())
TreeSet<Integer> ts = new TreeSet<>(); ts.floor(x) // greatest โค x ts.ceiling(x) // smallest โฅ x ts.lower(x) // greatest < x ts.higher(x) // smallest > x ts.first() // min element ts.last() // max element ts.subSet(lo, hi) // [lo, hi) ts.headSet(hi) // < hi ts.tailSet(lo) // โฅ lo // LinkedHashMap โ insertion order preserved: Map<K,V> lhm = new LinkedHashMap<>(); // LinkedHashMap access-order (LRU Cache): new LinkedHashMap<>(capacity, 0.75f, true); // true=access-order
// 26 lowercase letters: int[] freq = new int[26]; for (char c : s.toCharArray()) freq[c - 'a']++; // 52 letters (upper + lower): int[] freq = new int[52]; idx = c>='a' ? c-'a' : c-'A'+26; // 10 digits: int[] freq = new int[10]; for (char c : s.toCharArray()) freq[c - '0']++; // 128 ASCII: int[] freq = new int[128]; for (char c : s.toCharArray()) freq[c]++; // Check two freq arrays equal: Arrays.equals(freq1, freq2)
// Unweighted undirected: List<List<Integer>> adj = new ArrayList<>(); for (int i = 0; i < n; i++) adj.add(new ArrayList<>()); for (int[] e : edges) { adj.get(e[0]).add(e[1]); adj.get(e[1]).add(e[0]); // omit for directed } // Weighted: List<List<int[]>> where int[]{neighbor, weight} List<List<int[]>> adj = new ArrayList<>(); for (int i = 0; i < n; i++) adj.add(new ArrayList<>()); adj.get(u).add(new int[]{v, w}); // In-degree array (for Kahn's topo sort): int[] inDeg = new int[n]; for (int[] e : edges) inDeg[e[1]]++;
// 1D DP: int[] dp = new int[n + 1]; // zeros int[] dp = new int[n + 1]; Arrays.fill(dp, Integer.MAX_VALUE); // infinity dp[0] = 0; // base case // 2D DP: int[][] dp = new int[m + 1][n + 1]; for (int[] row : dp) Arrays.fill(row, (int) 1e9); // Memoization (top-down): int[] memo = new int[n]; Arrays.fill(memo, -1); // -1 = uncomputed // Boolean DP: boolean[] dp = new boolean[n + 1]; dp[0] = true; // base: empty subset
// Find minimum: int ans = Integer.MAX_VALUE; ans = Math.min(ans, candidate); // Find maximum: int ans = Integer.MIN_VALUE; ans = Math.max(ans, candidate); // Count valid items: int count = 0; count++; // Collect results: List<List<Integer>> res = new ArrayList<>(); res.add(new ArrayList<>(current)); // snapshot! // Global variable for DFS/recursion: int[] ans = {0}; // int[] tricks lambda // OR: use instance variable / int field // Two-variable result: int[] res = {start, end}; return new int[]{min, max};
// Integer.parseInt can throw NumberFormatException! try { int n = Integer.parseInt(s); } catch (NumberFormatException e) { /* handle */ } // Parse with radix: Integer.parseInt("FF", 16) // hex โ 255 Integer.parseInt("1010", 2) // binary โ 10 // Convert to different base: Integer.toString(255, 16) // "ff" Integer.toString(10, 2) // "1010" // Null-safe comparison: Objects.equals(a, b) // handles null // Autoboxing pitfall: Integer a = 1000, b = 1000; a == b // โ false (reference compare above 127!) a.equals(b) // โ true
a + b silently wraps when sum exceeds 2.1B. Happens in prefix sums, DP transitions, coordinates.Fix: Cast to long before adding.
// โ Overflows silently int sum = a + b; // โ Use long long sum = (long) a + b; // โ Safe overflow check (before adding) if (a > Integer.MAX_VALUE - b) /* would overflow */; // Common in: prefix sum, dp[i-1]+dp[i-2], Fibonacci large n
width * height โ each fits in int but product doesn't. Very common in area/volume problems and mod multiplication.// โ Both ints, product overflows int area = width * height; // โ Cast one operand first long area = (long) width * height; // โ Modular multiply int r = (a * b) % MOD; // โ int r = (int)((long) a * b % MOD); // Common in: matrix chain DP, coordinate geometry, hashing
(lo + hi) / 2 overflows when both are near MAX_VALUE. Use subtraction form instead.// โ Can overflow when lo + hi > Integer.MAX_VALUE int mid = (lo + hi) / 2; // โ Safe int mid = lo + (hi - lo) / 2; // โ Also safe (unsigned right shift) int mid = (lo + hi) >>> 1;
-Integer.MIN_VALUE == Integer.MIN_VALUE and Math.abs(Integer.MIN_VALUE) is still negative!Integer.MIN_VALUE // -2147483648 -Integer.MIN_VALUE // -2147483648 โ still negative! Math.abs(Integer.MIN_VALUE) // -2147483648 โ still negative! // โ Safe abs for int: long abs = Math.abs((long) n); // Comparator: never use (a - b) when a or b can be MIN/MAX // โ Use Integer.compare(a, b) instead
dist[u] = MAX_VALUE; then dist[u] + w overflows to negative, incorrectly passing the comparison.// โ dist[u] = MAX_VALUE, dist[u] + w overflows negative if (dist[u] + w < dist[v]) ... // โ Option 1: guard before adding if (dist[u] != Integer.MAX_VALUE && dist[u] + w < dist[v]) ... // โ Option 2: use a safe sentinel value int INF = (int) 1e9; // 1e9 + 1e9 = 2e9, still fits in int (MAX ~2.1B)
nums[0] or nums.length - 1 on null or empty array throws immediately.// Always guard at the very top if (nums == null || nums.length == 0) return ...; // Also: nums.length < 2 when algo needs at least 2 elements if (nums.length < 2) return nums.length == 0 ? 0 : nums[0]; // Kadane's: initialize with nums[0], loop from i=1 โ needs guard // Two pointers: int right = nums.length-1 โ -1 if empty โ crash // Fixed window: k > n โ index out of bounds building first window
s.charAt(0), s.substring(1) on empty string throw. s.length()-1 = -1 as array index crashes.if (s == null || s.isEmpty()) return ...; // "".split(",") returns [""] (length=1), not [] ! String[] p = "".split(","); // p.length = 1, p[0] = "" // Valid Palindrome: s with only spaces/symbols โ after stripping // alphanumerics = empty โ return true (empty is palindrome) // Group Anagrams: empty string "" โ sorted key = "" โ valid group
head.val or head.next when head == null. Fast pointer's fast.next.next without checking fast.next first.if (head == null) return null; if (head.next == null) return head; // single node // Fast/slow: check order matters (short-circuit evaluation) while (fast != null && fast.next != null) // fast checked BEFORE fast.next โ no NPE // Dummy head eliminates null checks for head deletion ListNode dummy = new ListNode(0); dummy.next = head;
null as the base case. Every BFS must guard before starting.// DFS base case โ ALWAYS first line if (node == null) return 0; // or null/false/empty // BFS if (root == null) return result; // Don't offer null children to queue if (node.left != null) q.offer(node.left); if (node.right != null) q.offer(node.right);
// Kadane: max subarray of [โ5] = โ5 โ (init from nums[0]) // Max Product of [โ5] = โ5 โ (init maxProd = minProd = nums[0]) // House Robber [3] = 3 โ // Binary Search [5], find 5: lo=hi=0, mid=0, found โ // Two pointers: left=right=0, while(left<right) never runs โ OK // House Robber II: [5] โ must return nums[0], not run circular logic if (nums.length == 1) return nums[0];
// Middle of [1โ2]: slow=1, fast=2 โ fast.next=null โ slow=1 โ // Remove 2nd from end of [1โ2]: removes head โ need dummy node // House Robber II [2,3]: can't rob both โ max(2,3) = 3 if (nums.length == 2) return Math.max(nums[0], nums[1]); // Palindrome Linked List [1โ2]: slow=1, fast=2, reverse [2] // compare: 1==2? no โ not palindrome โ // [1โ1]: compare: 1==1? yes โ palindrome โ
// Diameter [1]: left=right=0, ans=max(0,0+0)=0 โ // Max Depth [1]: 1+max(0,0) = 1 โ // Path Sum [5], target=5: leaf check passes โ // BST validate single node: [0] with int bounds // โ node.val = Integer.MIN_VALUE fails int bounds! // โ Always use long bounds: validate(root, Long.MIN_VALUE, Long.MAX_VALUE); // LCA: if root == p or root == q, return root immediately
// 3Sum all zeros [0,0,0] โ should return [[0,0,0]], not multiple copies // Requires: skip nums[i]==nums[i-1] AND skip duplicates in inner loop // Rotated Sorted Array II all same [1,1,1,1] // Can't determine sorted half โ linear scan fallback: if (nums[lo]==nums[mid] && nums[mid]==nums[hi]) { lo++; hi--; } // Longest Consecutive [1,1,1] โ answer=1 (not 3) // HashSet deduplicates; count only from sequence start
0 when all elements are negative โ valid answer is negative but 0 is returned.// โ Kadane's: init maxSoFar = 0 // Input [-3,-1,-2] โ wrongly returns 0, correct is -1 // โ Init from first element int maxSoFar = nums[0], curr = nums[0]; // Max Product Subarray [-2] โ -2, not 0 // Init maxProd = minProd = ans = nums[0] // Container With Most Water: heights usually โฅ 0 per constraints // Always reread: what are the value bounds?
// Max Product Subarray [2,3,0,4]: // Zero resets the subarray โ treat zero as new starting point // maxProd = max(num, maxProd*num, minProd*num) handles it // Product Except Self: // 1 zero: only the zero's position has nonzero product // 2+ zeros: all positions = 0 (no need to special-case if logic is right) // Subarray Sum = 0: valid answer exists // prefixCount.put(0, 1) handles prefix sums that equal target exactly
// [1,2,3,4,5] โ "rotated" by 0 positions // nums[mid] > nums[hi] is false โ right half check branch // Binary search still works correctly โ // Find Min in Rotated: lo=0 after findPivot โ pivot=0 โ min=nums[0] // 3Sum on sorted input: valid, but add early exit: if (nums[i] > 0) break; // no three positives sum to 0 // Dutch National Flag [0,0,0,0]: all low, mid goes to end
if (k > nums.length) return 0; // or handle per problem // k = n: sliding loop [k, n) never executes โ returns first window result โ // k = 1: window is single element โ trivial, check still passes โ // Permutation In String: p.length() > s.length() โ false immediately if (p.length() > s.length()) return false;
"", not null. Max window โ entire string may be valid answer.// Min Window Substring: no valid window int minLen = Integer.MAX_VALUE, start = 0; return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen); // Longest Substring No Repeat: all same chars "aaaa" // Window shrinks to 1 on every step โ answer = 1 โ // Max Consecutive Ones III: all ones โ answer = n โ // No ones (k=0, all zeros) โ answer = 0 โ (window size=0)
matches only when a character transitions across the zero boundary.// Add right char: increment matches only when freq hits exactly 0 if (--need[c] == 0) matches++; // Remove left char: decrement matches when freq goes from 0 to 1 if (++need[c] == 1) matches--; // โ Wrong: incrementing matches every time character is seen // โ overcounts when character appears more than needed // Window valid when: matches == number of distinct chars in pattern
lo + 1 == hi, mid = lo. If you set lo = mid, you make no progress โ infinite loop.// โ No progress when lo=0, hi=1: mid=0, lo stays 0 while (lo < hi) { int mid = lo + (hi - lo) / 2; if (valid(mid)) hi = mid; else lo = mid; // โ INFINITE LOOP! } // โ Always advance by at least 1 else lo = mid + 1; // Rule: when lo can equal hi, use while(lo < hi) // Rule: when searching for exact match, use while(lo <= hi)
// First occurrence (leftmost): keep searching LEFT when found if (nums[mid] == target) hi = mid; // โ not return! else if (nums[mid] < target) lo = mid+1; else hi = mid-1; // loop ends: lo is first occurrence (if exists) // Last occurrence (rightmost): keep searching RIGHT when found if (nums[mid] == target) lo = mid+1; // โ not return! else if (nums[mid] < target) lo = mid+1; else hi = mid-1; // answer is lo-1 (= hi after loop)
// Koko Eating Bananas: // lo = 1 (minimum possible rate) // hi = max(piles) (one pile per hour in worst case) // Capacity to Ship: // lo = max(weights) (must fit heaviest package) // hi = sum(weights) (ship everything in one day) // isFeasible must be strictly monotone: // if mid works โ all values โฅ mid also work (for minimize-max) // Verify this property before using binary search on answer!
fast.next.next before fast.next != null throws NPE for even-length lists.// โ NPE when list has even length (fast.next == null) while (fast.next.next != null) ... // โ Short-circuit: fast checked before fast.next while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } // Odd [1,2,3]: fast=3, slow=2 (2nd node = middle) โ // Even [1,2,3,4]: fast=null, slow=2 (first of two middles) โ
curr.next = prev before saving curr.next permanently drops the rest of the list.// โ curr.next overwritten, rest of list lost curr.next = prev; prev = curr; curr = curr.next; // โ already points to prev now! // โ Save first ListNode nxt = curr.next; // 1. save curr.next = prev; // 2. reverse prev = curr; // 3. advance prev curr = nxt; // 4. advance curr
// โ Dummy node avoids special case for head removal ListNode dummy = new ListNode(0); dummy.next = head; ListNode fast = dummy, slow = dummy; for (int i = 0; i <= n; i++) fast = fast.next; // n+1 steps while (fast != null) { fast = fast.next; slow = slow.next; } slow.next = slow.next.next; return dummy.next; // [1,2], n=2: removes node 1 (head) โ dummy.next = node2 โ
// Phase 1: do fast and slow meet? // Phase 2: find entry point ListNode ptr = head; while (ptr != slow) { ptr = ptr.next; slow = slow.next; } // ptr == slow == cycle start // Edge: cycle at head (tail.next = head) // Edge: cycle at second node // Edge: no cycle โ fast reaches null, return false/null
// Mention iterative solution for very large inputs // Iterative inorder (safe for skewed): Deque<TreeNode> stk = new ArrayDeque<>(); TreeNode cur = root; while (cur != null || !stk.isEmpty()) { while (cur != null) { stk.push(cur); cur = cur.left; } cur = stk.pop(); /* visit cur */ cur = cur.right; } // Balanced binary tree: max depth = log(n) โ recursion safe โ
Integer.MIN_VALUE or Integer.MAX_VALUE. Using int bounds in recursive validation fails.// โ Node with value = Integer.MIN_VALUE // validate(node, Integer.MIN_VALUE, parent) โ node.val <= min fails! // โ Always use Long bounds boolean validate(TreeNode n, long lo, long hi) { if (n == null) return true; if (n.val <= lo || n.val >= hi) return false; return validate(n.left, lo, n.val) && validate(n.right, n.val, hi); } validate(root, Long.MIN_VALUE, Long.MAX_VALUE);
// โ ans = 0 fails for tree [-3] int ans = 0; // โ Init to smallest possible int ans = Integer.MIN_VALUE; // or root.val // Max gain from subtree: skip negative contribution int L = Math.max(0, maxGain(n.left)); int R = Math.max(0, maxGain(n.right)); // Path Sum with negative target: don't prune on remaining < 0 // Must traverse to actual leaf before checking
// If root == p or root == q โ return root (it's the LCA) if (root == null || root == p || root == q) return root; TreeNode L = lca(root.left, p, q); TreeNode R = lca(root.right, p, q); // Both sides found something โ this node is LCA if (L != null && R != null) return root; return L != null ? L : R; // BST LCA: simpler โ use value comparisons to navigate
// โ Only visits component containing node 0 dfs(graph, 0, visited); // โ Start from every unvisited node for (int i = 0; i < n; i++) if (!visited[i]) { dfs(graph, i, visited); components++; } // Topo sort (Kahn's): if result.size() < n โ cycle exists // (works correctly even with disconnected components because // all nodes with in-degree 0 are enqueued initially)
Bug 2: Dijkstra is incorrect with negative edges โ use Bellman-Ford.
// โ Stale-entry guard (must have!) int[] curr = pq.poll(); if (curr[0] > dist[curr[1]]) continue; // skip outdated // Unreachable nodes: dist stays Integer.MAX_VALUE (or INF) return dist[dst] == (int)1e9 ? -1 : dist[dst]; // โ Dijkstra with negative weights โ use Bellman-Ford instead
// โ Treats parent edge as cycle โ false positive if (visited[neighbor]) return true; // cycle! // โ Ignore the parent in undirected DFS if (neighbor == parent) continue; if (visited[neighbor]) return true; // real cycle // For directed graphs: use 3-state visited (unvisited/in-stack/done) // "in-stack" state catches directed back-edges (true cycles)
stack.pop() on empty stack throws EmptyStackException (Stack class) or NoSuchElementException (Deque).// โ Guard before every pop/peek if (!stack.isEmpty()) stack.pop(); // Valid Parentheses: extra ")" โ stack empty โ return false if (stack.isEmpty() || stack.pop() != expected) return false; // Basic Calculator: sign before first number // Initialize: stack.push(0) as result, push(1) as sign // Handles leading "+" or "-" before first operand
// Next Greater Element: remaining have no NGE โ result[i] = -1 while (!stack.isEmpty()) result[stack.pop()] = -1; // Daily Temperatures: remaining โ answer already 0 (default) โ // Largest Rectangle Histogram: flush remaining with sentinel int[] h = Arrays.copyOf(heights, heights.length + 1); // h[n]=0 // The sentinel height=0 triggers all remaining bars to pop
dp[i-2] when i=1, or nums[i-1] in dp when i=0.// Climbing Stairs: n=1 โ dp[i-2] accesses dp[-1] if (n <= 1) return n; // Decode Ways: s[0]='0' โ 0 ways immediately // dp[1] = s.charAt(0) != '0' ? 1 : 0 (must check!) // Coin Change: amount=0 โ answer is 0 (no coins needed) if (amount == 0) return 0; // Edit Distance: dp[i][0]=i (delete i chars), dp[0][j]=j (insert j) // Don't forget to initialize these boundary rows/cols!
// [1,2,3] circular: can't rob 1 and 3 together if (n == 1) return nums[0]; if (n == 2) return Math.max(nums[0], nums[1]); int r1 = rob(nums, 0, n-2); // skip last int r2 = rob(nums, 1, n-1); // skip first return Math.max(r1, r2); // Max Product Subarray: circular not needed, but handle zero reset
// Unbounded (Coin Change): each coin reusable โ loop FORWARD for (int coin : coins) for (int j = coin; j <= amount; j++) dp[j] = Math.min(dp[j], dp[j-coin]+1); // 0/1 Knapsack: each item once โ loop BACKWARD for (int item : items) for (int j = W; j >= item.w; j--) dp[j] = Math.max(dp[j], dp[j-item.w]+item.v); // โ Coin Change with backward loop โ each coin used at most once // โ gives wrong (larger) answer for some inputs
curr directly โ all results in the list reference the same mutable object. All entries end up identical (usually empty).// โ All results point to same list result.add(curr); // โ Always copy on add result.add(new ArrayList<>(curr)); // For char[]: result.add(new String(charArr)) // For int[]: result.add(arr.clone()) // For String[]: result.add(Arrays.copyOf(arr, n)) // This bug produces result = [[], [], ...] (all empty lists) // instead of the actual subsets/permutations/paths
i > 0 && nums[i]==nums[i-1] skips too aggressively. Must use i > start to only skip at the same recursion level.// โ i > 0: skips valid combinations (too aggressive) if (i > 0 && nums[i] == nums[i-1]) continue; // โ i > start: only skip duplicates at the same level if (i > start && nums[i] == nums[i-1]) continue; // Also: MUST sort array first before this check works Arrays.sort(nums); // duplicates become adjacent // Applies to: Subsets II, Combination Sum II, Permutations II
== compares references. Two identical string objects that aren't the same instance return false.// โ Reference compare if (s1 == s2) ... // โ Value compare if (s1.equals(s2)) ... if (s1.equalsIgnoreCase(s2)) ... // String interning: string literals are cached, but // new String("abc") is never == another object // Integer autoboxing same pitfall: Integer a = 1000, b = 1000; a == b // false (not cached above 127) a.equals(b) // true โ
// "" (empty) โ palindrome โ true // " " (spaces) โ after stripping non-alnum โ empty โ true // "a" โ single char โ palindrome โ true // "ab" โ not palindrome // Valid Palindrome I: strip non-alphanumeric, lowercase, compare while (!Character.isLetterOrDigit(s.charAt(l))) l++; while (!Character.isLetterOrDigit(s.charAt(r))) r--; // Valid Palindrome II: skip one char (left OR right), check both
[""] (one empty string element)."".split(",").length // = 1, not 0 ! "".split(",")[0] // = "" (empty string) // โ Guard against empty string before split if (s.isEmpty()) return new String[0]; // trailing delimiter: "a,b,".split(",") โ ["a","b"] (2 elements) // to keep trailing empty: "a,b,".split(",", -1) โ ["a","b",""]
>> fills with the sign bit (1 for negatives). >>> always fills with 0. Counting bits on negative numbers needs >>>.-1 >> 1 // -1 (0xFFFFFFFF โ sign propagated) -1 >>> 1 // 2147483647 // Count bits on any int (including negative): // โ Use >>> so loop terminates while (n != 0) { count += n & 1; n >>>= 1; } // โ Or just: Integer.bitCount(n) handles all cases // Safe midpoint: (lo + hi) >>> 1 (unsigned, no overflow)
0 & (0-1) = 0 & -1 = 0 โ looks like power of 2 without the n > 0 guard. Also Integer.MIN_VALUE & (MIN_VALUE-1) = 0 โ same trap!// โ 0 falsely passes (and Integer.MIN_VALUE too) return (n & (n-1)) == 0; // โ Must check n > 0 return n > 0 && (n & (n-1)) == 0; // Kernighan's bit count: loop condition n != 0 // n = 0 โ loop never runs โ count = 0 โ (correct) // Get lowest set bit: n & (-n) // n = 0: 0 & 0 = 0 โ careful if used as loop guard
// Subarray length: j - i + 1 // Sliding window result: right - left + 1 (NOT right - left) // Substring s.substring(i, j): [i, j), length = j - i // โ Misses last element for (int i = 0; i < n-1; i++) // misses index n-1 // โ for (int i = 0; i < n; i++) // Two pointers: while(l < r) vs while(l <= r) // Binary search: while(lo <= hi) for exact-match // Binary search: while(lo < hi) for boundary-finding
% returns negative for negative operands. Python's always returns non-negative.// Java: -7 % 3 = -1 (not 2!) // Python: -7 % 3 = 2 (always non-negative) // โ Circular array backward step idx = (idx - 1) % n; // -1 when idx=0 // โ Always-positive modulo idx = ((idx - 1) % n + n) % n; idx = Math.floorMod(idx - 1, n); // Java 8+ built-in // โ Modular multiply (prevent overflow before mod) (int)((long) a * b % MOD)
(a, b) -> a - b overflows for large or negative values, violating the Comparator contract and causing random sorting.// โ Overflows: a = Integer.MIN_VALUE, b = 1 โ a-b = huge positive (a, b) -> a - b // โ Safe options: Integer.compare(a, b) (a, b) -> a < b ? -1 : a > b ? 1 : 0 Comparator.comparingInt(x -> x) // The error "Comparison method violates general contract" // is almost always this overflow bug
// Binary Search not found โ -1 // Min Window Substring no window โ "" // Coin Change impossible โ -1 (NOT 0!) // Gas Station impossible โ -1 // Subsets: empty set [] IS a valid result // Path Sum no valid path โ false // LCA p/q not in tree โ depends on problem statement // Coin Change: distinguish "0 coins needed (amount=0)" // from "impossible" (dp[amount] stays > amount) return dp[amount] > amount ? -1 : dp[amount];
// Array problems: if (nums == null || nums.length == 0) return ...; // String problems: if (s == null || s.isEmpty()) return ...; // Tree problems: if (root == null) return ...; // Linked List problems: if (head == null || head.next == null) return head; // Single element special cases: if (nums.length == 1) return nums[0]; // Window too large: if (k > nums.length) return 0; // Different lengths can't be anagrams: if (s.length() != t.length()) return false;
// โ Throws ConcurrentModificationException for (Integer k : map.keySet()) map.remove(k); // โ Use Iterator Iterator<Integer> it = map.keySet().iterator(); while (it.hasNext()) { it.next(); it.remove(); } // โ Or collect then remove List<Integer> toRemove = new ArrayList<>(); for (int k : map.keySet()) if (cond(k)) toRemove.add(k); toRemove.forEach(map::remove);
Unlock Full Access
Master DSA with step-by-step animations for every problem
- ๐ฌ 170+ animated visualizations
- ๐ All 17 topics covered
- ๐งฉ Patterns, Tricks & Edge Cases
- โพ๏ธ Lifetime updates included
๐ Secured by Razorpay ยท UPI, Cards, Netbanking accepted