๐ Recommended Learning Sequence
1. Arraysโ
2. Stringsโ
3. Matrixโ
4. Stackโ
5. Queueโ
6. Binary Searchโ
7. Linked Listโ
8. Greedyโ
9. Intervalsโ
10. Backtrackingโ
11. Treeโ
12. Heapโ
13. Graphโ
14. Dynamic Programmingโ
15. Bit Manipulationโ
16. Trieโ
17. Design
Priority:
P1 โ Must Do High interview frequency, core patterns
P2 โ Important Commonly asked, good to know
P3 โ Good to Know Warmup / low frequency
๐
๐ฆ
18 problems
Arrays
Basics ยท Two Pointers ยท Subarray ยท Hashing ยท Rotation ยท Sorting ยท Sliding Window
Basics
Two Pointers
Subarray ยท Sliding Window
Hashing
๐ค
17 problems
Strings
General ยท Sliding Window ยท Anagram ยท Palindrome
General
Sliding Window on Strings
Anagram
๐ฒ
9 problems
Matrix
2D Grid traversal, simulation, DFS/BFS on grid
๐
8 problems
Stack
Monotonic stack, expression evaluation, min stack
๐
13 problems
Binary Search
Classic search ยท Search on answer ยท Rotated array
๐
13 problems
Linked List
Reversal, cycle detection, merge, Floyd's
๐ก
5 problems
Greedy
Locally optimal choices
๐
5 problems
Intervals
Merge, insert, sweep line
๐ฟ
9 problems
Backtracking
Subsets, permutations, combinations, constraint solving
๐ณ
20 problems
Tree
BFS, DFS, BST, construction, path problems
โ
6 problems
Heap / Priority Queue
Top-K, two heaps, scheduling
๐ธ
11 problems
Graph
BFS/DFS, topo sort, shortest path, MST, Union-Find
๐ฏ
15 problems
Dynamic Programming
1D, 2D DP, LCS, knapsack, LIS
โก
8 problems
Bit Manipulation
XOR tricks, bit counting, Floyd's
๐
2 problems
Trie
Prefix tree, word search
๐
1 problem
Design
Data structure design
๐ช Sliding Window
๐ Two Pointers
โ Prefix Sum
๐ Binary Search
๐ Stack
๐ BFS/Queue
๐ Linked List
๐ณ Tree
๐ฟ Backtracking
๐ก Greedy
โ Heap
๐ธ Graph
๐ฏ DP
โก Bits
๐ช
2 patterns
Sliding Window
Maintain a contiguous subarray / substring window
๐
Fixed Sliding Window
Window of size k, slides one step at a time
ArrayStringโผ
When to Use
- Window size k is fixed (given in problem)
- Find max/min/average of every window of size k
- Count elements satisfying condition in each window
Maintain a window of fixed size k. Compute the first window, then slide by adding the next element and removing the leftmost โ avoid recomputing the whole window each time.
Complexity
Time O(n)Space O(1)
Problems
Java Template
int windowSum = 0; // Build first window for (int i = 0; i < k; i++) windowSum += nums[i]; int maxSum = windowSum; // Slide: add right, remove left for (int i = k; i < nums.length; i++) { windowSum += nums[i] - nums[i - k]; maxSum = Math.max(maxSum, windowSum); } return maxSum;
Animation โ traces Java template (nums=[2,1,5,1,3,2], k=3, max sum)
1 / ?
nums
windowSum = 0
maxSum = 0
Build first window (i=0..2): windowSum = nums[0]+nums[1]+nums[2]
โ๏ธ
Variable (Dynamic) Sliding Window
Expand right, shrink left when constraint violated
ArrayStringโผ
When to Use
- Find smallest/largest window satisfying a condition
- Condition can be checked in O(1) or O(alphabet)
- Window shrink is valid (removing left never breaks monotonicity)
- Problems: min window, longest without repeat, anagram
Expand the window by moving the right pointer; when a constraint is violated (e.g., duplicate), shrink from the left until valid again. Track the best window size seen.
Complexity
Time O(n)Space O(k) map
Java Template
int left = 0, result = 0; Map<Character, Integer> window = new HashMap<>(); for (int right = 0; right < s.length(); right++) { // 1. Expand: add s[right] to window window.merge(s.charAt(right), 1, Integer::sum); // 2. Shrink: while window invalid, move left while (windowInvalid(window)) { window.merge(s.charAt(left), -1, Integer::sum); if (window.get(s.charAt(left)) == 0) window.remove(s.charAt(left)); left++; } // 3. Update result (window = [left..right]) result = Math.max(result, right - left + 1); } return result;
Animation โ traces Java template (s="pwwkew", LC 3 โ Longest Substring Without Repeating)
1 / ?
s
left = 0
right = 0
result = 0
Expand right: add s[right]. If duplicate โ shrink left until valid.
๐
3 patterns
Two Pointers
One or two index pointers scanning array/string
โบ
Opposite Ends
left=0, right=n-1, converge toward center
Sorted ArrayStringโผ
When to Use
- Array is sorted (or can be sorted)
- Find pair with target sum / max area
- Palindrome check
- Trap water, squares of sorted array
Start with left=0, right=n-1. If sum < target, move left right (need larger). If sum > target, move right left (need smaller). Converge until found or pointers meet.
Complexity
Time O(n)Space O(1)
Java Template
int left = 0, right = nums.length - 1; while (left < right) { int sum = nums[left] + nums[right]; if (sum == target) { // found answer break; } else if (sum < target) { left++; // need larger sum } else { right--; // need smaller sum } }
Animation โ traces Java template (Two Sum II: nums=[2,7,11,15], target=9)
1 / ?
nums
sum = 0
target = 9
โโ
Same Direction (Slow/Fast)
slow writes valid values, fast scans ahead
Arrayโผ
When to Use
- Remove duplicates / filter in-place
- Merge two sorted arrays
- slow = write pointer, fast = read pointer
Slow marks the next valid write position; fast scans ahead. When fast finds a new unique value, copy it to slow and advance slow. Skip duplicates without copying.
Java Template
int slow = 0; for (int fast = 1; fast < nums.length; fast++) { if (nums[fast] != nums[slow]) { slow++; nums[slow] = nums[fast]; } } return slow + 1; // new length
Animation โ traces Java template (Remove Duplicates: nums=[1,1,2,2,3])
1 / ?
nums
slow = 0
fast = 0
๐บ
Sort + Fix One + Two Pointers
Fix first element, use two pointers for remaining sum
Arrayk-Sumโผ
When to Use
- 3Sum, 4Sum โ find k numbers summing to target
- Sort first: O(n log n), then O(nยฒ) total
- Skip duplicates to avoid duplicate triplets
Sort the array, then fix nums[i] and run two pointers (left=i+1, right=n-1) on the rest. If sum<0 move left right; if sum>0 move right left. Skip duplicate i to avoid repeating triplets.
Problems
Java Template (3Sum)
Arrays.sort(nums); for (int i = 0; i < nums.length - 2; i++) { if (i > 0 && nums[i] == nums[i-1]) continue; // skip dup int left = i+1, right = nums.length-1; while (left < right) { int sum = nums[i] + nums[left] + nums[right]; if (sum == 0) { res.add(Arrays.asList(nums[i],nums[left],nums[right])); while(left<right && nums[left]==nums[left+1]) left++; while(left<right && nums[right]==nums[right-1]) right--; left++; right--; } else if (sum < 0) left++; else right--; } }
Animation โ traces Java template (3Sum: nums=[-1,0,1,2,-1,-4] sorted, target=0)
1 / ?
nums
i = 0
sum = 0
triplets = []
โ
3 patterns
Prefix Sum & Hashing
Precompute cumulative values; use HashMap for O(1) lookup
โ
Prefix Sum Array
prefix[i] = sum of nums[0..i-1]
Arrayโผ
When to Use
- Sum of subarray [i..j] in O(1): prefix[j+1] - prefix[i]
- Product of array except self (prefix + suffix product)
- Count subarrays with sum = k (pair with HashMap)
Maintain running sum; for each position, if (sum - k) was seen before, those prefixes form subarrays ending here that sum to k. Use HashMap to count prefix sums.
Java Template (sum=k count)
Map<Integer,Integer> prefixCount = new HashMap<>(); prefixCount.put(0, 1); // empty prefix int sum = 0, count = 0; for (int num : nums) { sum += num; // if (sum - k) seen before, those subarrays sum to k count += prefixCount.getOrDefault(sum - k, 0); prefixCount.merge(sum, 1, Integer::sum); } return count;
Animation โ traces template (Subarray Sum=K: nums=[1,2,-1,2], k=2)
1 / ?
nums
pfx
sum = 0
count = 0
๐
Frequency HashMap
Count occurrences, detect duplicates, group by key
HashMapHashSetโผ
When to Use
- Check if element exists / count frequency in O(1)
- Group elements (anagrams, isomorphic)
- Find missing / duplicate elements
- Longest consecutive sequence
Store valueโindex in a HashMap as you scan. For each nums[i], check if (target - nums[i]) exists in seen โ if so, you found the pair. Otherwise add nums[i] to seen.
Java Template
// Count frequencies Map<Integer,Integer> freq = new HashMap<>(); for (int n : nums) freq.merge(n, 1, Integer::sum); // Existence check (Two Sum style) Map<Integer,Integer> seen = new HashMap<>(); for (int i = 0; i < nums.length; i++) { int complement = target - nums[i]; if (seen.containsKey(complement)) return new int[]{seen.get(complement), i}; seen.put(nums[i], i); }
Animation โ traces template (Two Sum: nums=[2,7,11,15], target=9)
1 / ?
nums
seen = {}
๐
Kadane's Algorithm
Maximum subarray sum in O(n) โ local vs global max
ArrayDPโผ
When to Use
- Max/min sum contiguous subarray
- Max profit (buy/sell stock)
- At each step: extend current subarray or restart
currMax = max(nums[i], currMax + nums[i]) โ either extend the current subarray or start fresh. Track global maxSoFar. One pass, O(n).
Java Template
int maxSoFar = nums[0], currMax = nums[0]; for (int i = 1; i < nums.length; i++) { // Extend or restart subarray currMax = Math.max(nums[i], currMax + nums[i]); maxSoFar = Math.max(maxSoFar, currMax); } return maxSoFar;
Animation โ traces template (Maximum Subarray: nums=[-2,1,-3,4,-1,2,1])
1 / ?
nums
currMax = 0
maxSoFar = 0
๐
2 patterns
Binary Search
Halve search space each step โ O(log n)
โ๏ธ
Classic Binary Search
Find target / boundary in sorted array
Sorted Arrayโผ
When to Use
- Array is sorted (or partially sorted)
- Find exact value / leftmost / rightmost occurrence
- Find peak element, minimum in rotated array
- Search in rotated sorted array
Halve the search space each step: mid = (lo+hi)/2. If nums[mid]<target, search right (lo=mid+1); if nums[mid]>target, search left (hi=mid-1). Exit when found or lo>hi.
Complexity
Time O(log n)Space O(1)
Java Template
int lo = 0, hi = nums.length - 1; while (lo <= hi) { int mid = lo + (hi - lo) / 2; if (nums[mid] == target) { return mid; } else if (nums[mid] < target) { lo = mid + 1; } else { hi = mid - 1; } } return -1; // Find leftmost: when found, hi = mid-1 (keep searching left) // Find rightmost: when found, lo = mid+1 (keep searching right)
Animation โ traces Java template (nums=[1,3,5,7,9], target=5)
1 / ?
nums
lo = 0
hi = 0
mid = 0
๐ฏ
Binary Search on Answer (Search Space)
Binary search on the value/range, not array index
OptimizationFeasibilityโผ
When to Use
- "Minimize the maximum" or "maximize the minimum"
- Answer lies in a monotone feasibility space
- Can write
boolean isFeasible(mid)in O(n) - Key: if mid is feasible, smaller/larger might be too
Binary search on the answer range [minPossible..maxPossible]. Test mid with isFeasible(mid). If feasible, try smaller (hi=mid-1); else try larger (lo=mid+1). Return best feasible.
Problems
#875 Koko Eating Bananas M
#1011 Capacity to Ship Packages M
#410 Split Array Largest Sum H
GFG Allocate Min Pages
IB Painters Partition
Java Template
int lo = minPossible, hi = maxPossible; int ans = hi; while (lo <= hi) { int mid = lo + (hi - lo) / 2; if (isFeasible(mid)) { ans = mid; // valid, try smaller hi = mid - 1; } else { lo = mid + 1; // not valid, need larger } } return ans; // isFeasible: greedily check if mid value is achievable boolean isFeasible(int mid) { int chunks = 1, curr = 0; for (int x : arr) { if (curr + x > mid) { chunks++; curr = 0; } curr += x; } return chunks <= k; }
Animation โ traces template (Split Array: arr=[1,2,3,4,5], k=2, min max sum)
1 / ?
lo = 5
hi = 15
mid = -
ans = 15
๐
3 patterns
Stack Patterns
LIFO โ simple, monotonic increasing/decreasing
๐ฅ
Simple Stack
Matching pairs, undo/redo, evaluate expressions
StringExpressionโผ
When to Use
- Matching brackets / parentheses
- Evaluate postfix / infix expressions
- Maintain min/max with O(1) queries
- Undo last operation
Push opening brackets; on closing bracket, pop and check if it matches. If stack empty on close or leftover after scan โ invalid. LIFO ensures correct nesting.
Java Template (bracket match)
Deque<Character> stack = new ArrayDeque<>(); for (char c : s.toCharArray()) { if (c == '(' || c == '[' || c == '{') { stack.push(c); } else { if (stack.isEmpty()) return false; char top = stack.pop(); if (!matches(top, c)) return false; } } return stack.isEmpty();
Animation โ traces Java template (Valid Parentheses: s="({[]})")
1 / ?
s
stack
๐
Monotonic Decreasing Stack
Pop when current > top โ find Next Greater Element
Next GreaterTemperaturesโผ
When to Use
- Find next greater element to the right
- Stack maintains indices of elements in decreasing order
- When
nums[i] > nums[stack.top()]: pop โiis the NGE for popped index - Daily temperatures, next larger value queries
Keep indices in decreasing value order. When nums[i]>top, pop โ i is the next greater element for the popped index. Push i after each pop phase.
Java Template
int[] result = new int[n]; Deque<Integer> stack = new ArrayDeque<>(); // indices for (int i = 0; i < n; i++) { // While stack has smaller elements, current is NGE while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) { int idx = stack.pop(); result[idx] = nums[i]; // or i - idx for distance } stack.push(i); } // Remaining in stack have no NGE โ result stays 0 or -1
Animation โ traces Java template (Next Greater: nums=[2,1,3,4,2])
1 / ?
i
nums
NGE
stack
๐
Monotonic Increasing Stack
Pop when current < top โ find largest rectangle
HistogramRectangleโผ
When to Use
- Find largest rectangle / area bounded by bars
- Stack maintains indices in increasing height order
- When
height[i] < height[stack.top()]: pop โ calculate area - Width =
i - stack.peek() - 1(distance to previous smaller)
Keep indices in increasing height order. When height[i]<top, pop and compute area (height ร width). Width = distance to prev smaller. Sentinel 0 flushes remaining bars.
Java Template
Deque<Integer> stack = new ArrayDeque<>(); int maxArea = 0; // Append sentinel 0 to flush remaining bars int[] h = Arrays.copyOf(heights, heights.length + 1); for (int i = 0; i < h.length; i++) { while (!stack.isEmpty() && h[i] < h[stack.peek()]) { int height = h[stack.pop()]; int width = stack.isEmpty() ? i : i - stack.peek() - 1; maxArea = Math.max(maxArea, height * width); } stack.push(i); } return maxArea;
Animation โ traces Java template (Largest Rectangle: heights=[2,1,5,6,2,3])
1 / ?
i
h
maxArea = 0
๐
2 patterns
BFS / Queue
Level-by-level exploration โ shortest path on unweighted graphs
๐ต
Standard BFS (Level Order)
Process nodes level by level using a queue
GraphTreeGridโผ
When to Use
- Find shortest path in unweighted graph/grid
- Level-order tree traversal
- Explore all neighbors before going deeper
- Clone graph, course schedule (cycle detect)
Enqueue start, mark visited. Process level by level: poll node, add unvisited neighbors to queue and visited. First time a node is reached = shortest path (unweighted).
Complexity
Time O(V+E)Space O(V)
Java Template
Queue<Integer> queue = new LinkedList<>(); Set<Integer> visited = new HashSet<>(); queue.offer(start); visited.add(start); while (!queue.isEmpty()) { int size = queue.size(); // process one level for (int i = 0; i < size; i++) { int node = queue.poll(); // process node for (int neighbor : graph.get(node)) { if (!visited.contains(neighbor)) { visited.add(neighbor); queue.offer(neighbor); } } } level++; }
Animation โ BFS on graph 0โ{1,2}, 1โ{0,3}, 2โ{0,3}, 3โ{1,2}
1 / ?
0
2
1
3
๐ก in queue ย ๐ข visited ย Edges: 0โ1,2 ย 1โ3 ย 2โ3
queue = [0]
visited = {0}
๐ด
Multi-Source BFS
Start BFS from multiple sources simultaneously
GridDistanceโผ
When to Use
- Multiple starting points spread simultaneously
- Find min distance from any source to each cell
- Enqueue all sources first, then run BFS
Push all source nodes into the queue before the main loop. Then run standard BFS โ wavefront expands from all sources simultaneously.
Java Template
Queue<int[]> q = new LinkedList<>(); // Enqueue ALL sources first for (int r = 0; r < rows; r++) for (int c = 0; c < cols; c++) if (grid[r][c] == SOURCE) q.offer(new int[]{r, c}); int[] dirs = {-1,0,1,0,-1}; while (!q.isEmpty()) { int[] cell = q.poll(); for (int d = 0; d < 4; d++) { int nr = cell[0]+dirs[d], nc = cell[1]+dirs[d+1]; if (inBounds(nr,nc) && grid[nr][nc] == FRESH) { grid[nr][nc] = ROTTEN; q.offer(new int[]{nr, nc}); } } }
Animation โ Rotting Oranges: 2ร2 grid, 2 rotten (R), 2 fresh (F)
1 / ?
time = 0
๐
3 patterns
Linked List
Fast/slow pointers, in-place reversal, merge
๐ข๐
Fast / Slow Pointers (Floyd's Cycle)
slow moves 1 step, fast moves 2 โ detect cycle, find middle
CycleMiddleโผ
When to Use
- Detect cycle in linked list
- Find middle node of list
- Find duplicate number (implicit linked list)
- Happy Number (detect cycle in sequence)
Slow moves 1 step, fast moves 2 steps per iteration. If there's a cycle, they must meet. If fast reaches null, no cycle. For middle: when fast at end, slow is middle.
Java Template
ListNode slow = head, fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) return true; // cycle! } return false; // Find middle: when fast reaches end, slow = middle // Find cycle start: after detection, reset one pointer // to head, move both 1 step โ they meet at cycle start
Animation โ Cycle detection: list 1โ2โ3โ4โ2 (cycle to node 2)
1 / ?
slow = 1
fast = 1
๐
In-Place List Reversal
Reverse entire list or a subrange using prev/curr/next
In-Placeโผ
When to Use
- Reverse whole list or range [left..right]
- Palindrome linked list check (reverse second half)
- Reverse K groups
- Reorder list (find middle, reverse second half, merge)
prev=null, curr=head. Each step: save next, point currโprev, advance prev=curr, curr=next. When curr is null, prev is the new head.
Java Template
ListNode prev = null, curr = head; while (curr != null) { ListNode next = curr.next; curr.next = prev; // reverse pointer prev = curr; curr = next; } return prev; // new head // Reverse range [left, right]: // 1. Walk to node before left // 2. Reverse (right-left+1) nodes // 3. Reconnect with dummy head
Animation โ Reverse: list 1โ2โ3โ4
1 / ?
prev = null
curr = 1
๐
Two-Pointer Merge / Remove Nth
Two passes or two pointers with offset
Two Pointersโผ
When to Use
- Remove Nth node from end: advance one pointer N steps first
- Merge two sorted lists
- Copy list with random pointer (HashMap for clones)
- Add Two Numbers (carry simulation)
Move fast n+1 steps ahead of slow. Then move both until fast reaches end โ slow will be one node before the nth-from-end. Skip that node.
Java Template (Remove Nth)
ListNode dummy = new ListNode(0); dummy.next = head; ListNode fast = dummy, slow = dummy; // Move fast n+1 steps ahead for (int i = 0; i <= n; i++) fast = fast.next; // Move both until fast reaches end while (fast != null) { fast = fast.next; slow = slow.next; } slow.next = slow.next.next; // skip nth node return dummy.next;
Animation โ Remove Nth From End: list 1โ2โ3โ4โ5, n=2
1 / ?
slow โ 1
fast โ 4
๐ณ
4 patterns
Tree Patterns
DFS (recursive), BFS (level order), path problems, BST
๐ฟ
DFS โ Recursive Traversal
Pre/In/Post-order; return value bubbles up from leaves
RecursionDFSโผ
Order Guide
- Preorder (rootโLโR): construct tree, serialize
- Inorder (LโrootโR): BST sorted order, kth smallest
- Postorder (LโRโroot): diameter, height, path sum
Process children first, then node. Return value (e.g. height) bubbles up. Use for diameter: max path through node = leftHeight + rightHeight.
Java Template
// Generic postorder โ result computed bottom-up int dfs(TreeNode node) { if (node == null) return 0; // base case int left = dfs(node.left); int right = dfs(node.right); // Update global answer if needed ans = Math.max(ans, left + right); return 1 + Math.max(left, right); // height } // Inorder BST (sorted): use int[] state = {k, result} void inorder(TreeNode node, int[] state) { if (node == null) return; inorder(node.left, state); if (--state[0] == 0) state[1] = node.val; inorder(node.right, state); }
Animation โ Postorder diameter: tree 1(2(4,5),3)
1 / ?
1
2
3
4
5
๐ active ย ๐ข done ย h= after visit
current = 1
diameter = 0
๐ถ
BFS โ Level Order Traversal
Queue; process one full level per outer loop iteration
QueueLevelโผ
When to Use
- Process tree level by level
- Right side view (last element of each level)
- Zigzag traversal (alternate direction)
- Average of levels
Use queue; capture size at start of each iteration = one level. Poll size nodes, add children. Repeat until queue empty.
Java Template
Queue<TreeNode> q = new LinkedList<>(); q.offer(root); while (!q.isEmpty()) { int size = q.size(); List<Integer> level = new ArrayList<>(); for (int i = 0; i < size; i++) { TreeNode node = q.poll(); level.add(node.val); if (node.left != null) q.offer(node.left); if (node.right != null) q.offer(node.right); } result.add(level); // one level collected }
Animation โ Level order: tree 1(2(4,5),3)
1 / ?
1
2
3
4
5
๐ก queued ย ๐ processing ย ๐ข done
queue = [1]
level = [1]
๐ค
Tree Path Problems
Root-to-leaf or any-node paths; track running sum
Path SumBacktrackโผ
When to Use
- Check if root-to-leaf path has target sum
- Collect all such paths (backtracking)
- Max path sum through any node (postorder + global max)
- Sum of root-to-leaf numbers
Add node to path, recurse left/right with updated (rem - val). At leaf, if rem==val add path to result. Backtrack: remove from path before returning.
Java Template
// Collect all root-to-leaf paths void dfs(TreeNode n, int rem, List<Integer> path) { if (n == null) return; path.add(n.val); if (n.left == null && n.right == null && rem == n.val) result.add(new ArrayList<>(path)); // found path dfs(n.left, rem - n.val, path); dfs(n.right, rem - n.val, path); path.remove(path.size() - 1); // backtrack } // Max path sum (any node to any node): int maxGain(TreeNode n) { if (n == null) return 0; int L = Math.max(0, maxGain(n.left)); int R = Math.max(0, maxGain(n.right)); ans = Math.max(ans, L + R + n.val); // path through n return Math.max(L, R) + n.val; }
Animation โ Path Sum II: tree 5(4(11(7,2),8(13,4)), target=22
path = [5,4,11]
rem = 22
DFS with backtracking: add node, recurse, remove. At leaf with rem==val โ valid path.
๐
BST Operations
Exploit BST property: left < node < right
BSTBoundsโผ
Key Techniques
- Validate BST: pass min/max bounds down recursively
- Construct BST from sorted array: pick midpoint as root
- Flatten to LL: preorder, point right to next, left = null
Validate: pass (min, max) down; node must be in (min,max). If null return true. Check node.val, then recurse left with (min, val) and right with (val, max).
Java Template (Validate BST)
boolean validate(TreeNode n, long min, long max) { if (n == null) return true; if (n.val <= min || n.val >= max) return false; return validate(n.left, min, n.val) && validate(n.right, n.val, max); } // Call: validate(root, Long.MIN_VALUE, Long.MAX_VALUE) // Construct balanced BST from sorted array: TreeNode build(int[] a, int lo, int hi) { if (lo > hi) return null; int mid = (lo + hi) / 2; TreeNode n = new TreeNode(a[mid]); n.left = build(a, lo, mid - 1); n.right = build(a, mid + 1, hi); return n; }
Animation โ Validate BST: 5(3,6) with 6(4,7) โ invalid (4<5)
Pass (min,max) bounds: root gets (-โ,โ). Left child gets (min, parent.val), right gets (parent.val, max). Fail if node.val outside bounds.
๐ฟ
3 patterns
Backtracking
Explore all possibilities; undo choice and try next
โ
Subsets / Power Set
Include or exclude each element
Choose/Skipโผ
When to Use
- Generate all subsets (2โฟ total)
- Avoid duplicates: sort + skip same value at same depth
- Start index increments to avoid re-using elements
Add current state to result. For each index from start: add nums[i], recurse with i+1, backtrack (remove). Subsets II: sort and skip nums[i]==nums[i-1] when i>start.
Java Template
void backtrack(int start, List<Integer> curr) { result.add(new ArrayList<>(curr)); // add every state for (int i = start; i < nums.length; i++) { // Skip duplicates at same level (Subsets II) if (i > start && nums[i] == nums[i-1]) continue; curr.add(nums[i]); backtrack(i + 1, curr); // i+1: no reuse curr.remove(curr.size() - 1); // undo } }
๐
Permutations
Use all elements in every order โ swap or visited array
All Ordersโผ
When to Use
- All arrangements of n distinct elements (n! total)
- Track used indices via
boolean[] used - Letter combinations (phone number)
When curr full, add to result. For each unused index: mark used, add to curr, recurse, backtrack (unmark, remove). All orders = try every unused element at each position.
Java Template
boolean[] used = new boolean[nums.length]; void backtrack(List<Integer> curr) { if (curr.size() == nums.length) { result.add(new ArrayList<>(curr)); return; } for (int i = 0; i < nums.length; i++) { if (used[i]) continue; used[i] = true; curr.add(nums[i]); backtrack(curr); curr.remove(curr.size()-1); used[i] = false; } }
โ๏ธ
Combinations + Pruning
Constraint-guided backtrack; prune dead branches early
PruningSum Targetโผ
When to Use
- Find combinations summing to target
- Prune: if
remaining < 0, stop immediately - Sort candidates to enable efficient pruning
- N-Queens, Sudoku Solver (constraint propagation)
If remain==0, add curr to result. For each candidate from start: if candidate>remain break (pruning). Add, recurse (reuse i if allowed), remove.
Java Template (Combination Sum)
void backtrack(int start, int remain, List<Integer> curr) { if (remain == 0) { result.add(new ArrayList<>(curr)); return; } for (int i = start; i < candidates.length; i++) { if (candidates[i] > remain) break; // pruning (sorted) curr.add(candidates[i]); backtrack(i, remain - candidates[i], curr); // reuse allowed curr.remove(curr.size()-1); } }
๐ก
2 patterns
Greedy
Make locally optimal choice at each step
๐
Sort + Greedy / Interval Scheduling
Sort by start or end time, greedily assign
IntervalsSortโผ
When to Use
- Minimum platforms / meeting rooms needed
- Sort by arrival; use min-heap of departure times
- Interval merging, activity selection
Two pointers on sorted arr/dep. arr[i]<=dep[j] means new arrival โ need platform. Else departure โ free platform. Track max platforms needed.
Problems
Java Template (Min Platforms)
Arrays.sort(arr); // sort arrivals Arrays.sort(dep); // sort departures int platforms = 1, maxPlatforms = 1; int i = 1, j = 0; while (i < n && j < n) { if (arr[i] <= dep[j]) { platforms++; i++; } else { platforms--; j++; } maxPlatforms = Math.max(maxPlatforms, platforms); } return maxPlatforms;
๐
Greedy Reach / Jump Game
Track max reachable index; minimize jumps to reach end
ArrayReachโผ
When to Use
- Can you reach the end? (Jump Game I)
- Minimum jumps to reach end? (Jump Game II)
- Track
maxReach,currEnd,jumps
maxReach = farthest we can go. When i reaches currEnd, we've exhausted current jump โ increment jumps, set currEnd = maxReach. One pass O(n).
Problems
Java Template (Jump Game II)
int jumps = 0, currEnd = 0, maxReach = 0; for (int i = 0; i < nums.length - 1; i++) { maxReach = Math.max(maxReach, i + nums[i]); if (i == currEnd) { // exhausted current jump jumps++; currEnd = maxReach; } } return jumps;
โ
3 patterns
Heap / Priority Queue
Efficiently maintain top-K, median, or K-way merge
๐
Top K Elements (Min-Heap of size K)
Keep min-heap of K; pop if size exceeds K
Min-HeapTop Kโผ
When to Use
- Kth largest: min-heap of size K (top = Kth largest)
- Top K frequent: heap on frequency
- K closest points: max-heap on distance
Min-heap of size K: add each element, if size>K poll (drops smallest). Peek = Kth largest. For top-K frequent: heap by frequency, same idea.
Complexity
Time O(n log k)Space O(k)
Java Template
// Kth largest โ min-heap of size k PriorityQueue<Integer> minHeap = new PriorityQueue<>(); for (int num : nums) { minHeap.offer(num); if (minHeap.size() > k) minHeap.poll(); // drop smallest } return minHeap.peek(); // peek = kth largest // Top K frequent: Map + heap on frequency Map<Integer,Integer> freq = new HashMap<>(); for (int n : nums) freq.merge(n, 1, Integer::sum); PriorityQueue<Integer> pq = new PriorityQueue<>( (a,b) -> freq.get(a) - freq.get(b)); for (int key : freq.keySet()) { pq.offer(key); if (pq.size() > k) pq.poll(); }
โ๏ธ
Two Heaps (Running Median)
Max-heap (lower half) + min-heap (upper half)
MedianBalanceโผ
When to Use
- Streaming median
- Lower half in max-heap, upper half in min-heap
- Rebalance: sizes differ by more than 1 โ transfer top
- Median = top of larger heap, or avg of both tops
Add to lo (max-heap), move lo's top to hi. If lo smaller than hi, move hi's top to lo. Median = lo.peek() or (lo.peek()+hi.peek())/2.
Java Template
PriorityQueue<Integer> lo = new PriorityQueue<>(Collections.reverseOrder()); // max PriorityQueue<Integer> hi = new PriorityQueue<>(); // min void addNum(int num) { lo.offer(num); hi.offer(lo.poll()); // balance: lo top โ hi if (lo.size() < hi.size()) lo.offer(hi.poll()); // keep lo >= hi in size } double findMedian() { return lo.size() > hi.size() ? lo.peek() : (lo.peek() + hi.peek()) / 2.0; }
๐
K-Way Merge
Min-heap holds current head of each of k sorted lists
MergeK Listsโผ
When to Use
- Merge K sorted linked lists or arrays
- Push first node of each list into min-heap
- Poll min, attach to result, push its next node
Push first node of each list into min-heap. Poll min, append to result, push its next. Repeat until heap empty. O(n log k) for n total nodes, k lists.
Complexity
Time O(n log k)Space O(k)
Problems
Java Template
PriorityQueue<ListNode> pq = new PriorityQueue<>( (a, b) -> a.val - b.val); for (ListNode list : lists) if (list != null) pq.offer(list); ListNode dummy = new ListNode(0), curr = dummy; while (!pq.isEmpty()) { ListNode node = pq.poll(); curr.next = node; curr = curr.next; if (node.next != null) pq.offer(node.next); } return dummy.next;
๐ธ
5 patterns
Graph Algorithms
BFS/DFS, Union-Find, Topo Sort, Shortest Path, MST
๐
Union-Find (Disjoint Set Union)
Path compression + union by rank โ near O(1) per op
ConnectivityMSTโผ
When to Use
- Check if two nodes are connected
- Count connected components
- Kruskal's MST (union cheapest edges)
- Detect cycle in undirected graph
Each node starts as its own parent. find(x) returns root (with path compression). union(x,y) links roots; use rank for balance. Same root = connected.
Java Template
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) { if (parent[x] != x) parent[x] = find(parent[x]); // path compression return parent[x]; } boolean union(int x, int y) { int px = find(x), py = find(y); if (px == py) return false; // already connected if (rank[px] < rank[py]) parent[px] = py; else if (rank[px] > rank[py]) parent[py] = px; else { parent[py] = px; rank[px]++; } return true; }
๐
Topological Sort (Kahn's BFS)
Process nodes with in-degree 0 first
DAGDependenciesโผ
When to Use
- Course prerequisite ordering
- Detect cycle in directed graph
- Build order for dependencies
- If result size < n โ cycle exists
Compute in-degrees. Start with nodes having in-degree 0. Process each: add to order, decrement neighbors' in-degrees; if 0, add to queue. Order size < n means cycle.
Java Template (Kahn's)
int[] inDegree = new int[n]; List<List<Integer>> adj = buildGraph(...); for (List<Integer> neighbors : adj) for (int nb : neighbors) inDegree[nb]++; Queue<Integer> q = new LinkedList<>(); for (int i = 0; i < n; i++) if (inDegree[i] == 0) q.offer(i); List<Integer> order = new ArrayList<>(); while (!q.isEmpty()) { int node = q.poll(); order.add(node); for (int nb : adj.get(node)) if (--inDegree[nb] == 0) q.offer(nb); } return order.size() == n; // false = cycle
๐บ
Dijkstra's Shortest Path
Min-heap greedy โ shortest path in weighted graph (non-negative)
Weighted GraphShortest Pathโผ
When to Use
- Shortest path from source in weighted graph
- All edge weights non-negative
- Network delay, cheapest path (without K limit)
Start with dist[src]=0. Use min-heap (dist, node). Poll smallest; for each neighbor, relax: if dist[u]+w < dist[v], update and push. Skip stale entries.
Complexity
Time O((V+E) log V)
Java Template
int[] dist = new int[n]; Arrays.fill(dist, Integer.MAX_VALUE); dist[src] = 0; PriorityQueue<int[]> pq = new PriorityQueue<>((a,b)->a[0]-b[0]); pq.offer(new int[]{0, src}); // {distance, node} while (!pq.isEmpty()) { int[] curr = pq.poll(); int d = curr[0], u = curr[1]; if (d > dist[u]) continue; // stale entry for (int[] edge : adj.get(u)) { int v = edge[0], w = edge[1]; if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; pq.offer(new int[]{dist[v], v}); } } }
โ๏ธ
Bellman-Ford / K-stop Variant
Relax all edges V-1 times; supports negative weights
Negative WeightsK Stepsโผ
When to Use
- Shortest path with negative edges
- Path with at most K stops (bounded Bellman-Ford)
- Copy prev dist array each iteration to limit to K edges
Relax all edges k+1 times. Use temp copy: updates in iteration i go into temp, so each path uses at most i edges. Handles negative weights.
Problems
Java Template (K-stop)
int[] prices = new int[n]; Arrays.fill(prices, Integer.MAX_VALUE); prices[src] = 0; for (int i = 0; i <= k; i++) { // k+1 iterations int[] temp = Arrays.copyOf(prices, n); for (int[] flight : flights) { int u=flight[0], v=flight[1], w=flight[2]; if (prices[u] != Integer.MAX_VALUE && prices[u] + w < temp[v]) temp[v] = prices[u] + w; } prices = temp; // use snapshot to limit stops } return prices[dst] == Integer.MAX_VALUE ? -1 : prices[dst];
๐
Prim's MST / Greedy Graph
Grow MST by always adding cheapest edge to unvisited node
MSTMin Costโผ
When to Use
- Minimum spanning tree (connect all nodes with min cost)
- Dense graphs (Prim's better than Kruskal's)
- Use min-heap on edge weights
Start from node 0. Min-heap stores (cost, node). Poll cheapest edge to unvisited node, add to MST, push its edges. Repeat until all connected.
Java Template
boolean[] visited = new boolean[n]; PriorityQueue<int[]> pq = new PriorityQueue<>((a,b)->a[0]-b[0]); pq.offer(new int[]{0, 0}); // {cost, node} int totalCost = 0; while (!pq.isEmpty()) { int[] curr = pq.poll(); int cost = curr[0], node = curr[1]; if (visited[node]) continue; visited[node] = true; totalCost += cost; for (int next = 0; next < n; next++) { if (!visited[next]) pq.offer(new int[]{dist(node, next), next}); } } return totalCost;
๐ฏ
5 patterns
Dynamic Programming
Optimal substructure + overlapping subproblems
๐
1D Linear DP
dp[i] depends on dp[i-1], dp[i-2], or dp[i-k]
FibonacciHouse Robberโผ
When to Use
- 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: curr = max(prev1, prev2+num). prev1 = best so far (skip or took prev), prev2 = best 2 back. Space O(1) by keeping only last two.
Java Template
// 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);
๐ฒ
2D Grid DP
dp[i][j] depends on dp[i-1][j] and dp[i][j-1]
GridPathsโผ
When to Use
- 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
dp[i][j] = paths to (i,j) = from top + from left. Init first row/col = 1. Fill row by row. With obstacles: set dp[i][j]=0 at obstacle.
Java Template
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];
๐ค
Two-Sequence DP (LCS / Edit Distance)
dp[i][j] = optimal for s1[0..i] and s2[0..j]
String DPLCSโผ
Recurrences
- 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
Edit Distance: if chars match, dp[i][j]=dp[i-1][j-1]. Else 1 + min(delete, insert, replace) = 1 + min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]).
Problems
Java Template (Edit Distance)
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];
๐
LIS โ Longest Increasing Subsequence
O(nยฒ) DP or O(n log n) patience sort
LISPatience Sortโผ
When to Use
- 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
Tails[i] = smallest tail of LIS of length i+1. For each num, binary search where it fits; replace or append. Length of tails = LIS length.
Java Template (O(n log n))
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();
โ๏ธ
Min/Max DP (Max Product Subarray)
Track both min and max at each step for sign flips
Negativesโผ
Key Insight
- 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)
Negative ร negative = positive. Track both minProd and maxProd; next max = max(n, maxรn, minรn), next min = min(n, maxรn, minรn).
Problems
Java Template
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;
โก
3 patterns
Bit Manipulation
XOR tricks, bit counting, arithmetic without operators
โ
XOR Tricks
a^a=0, a^0=a โ cancel pairs, find unique element
XORSingle Numberโผ
Key Properties
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 โ pairs cancel. Single unique remains. For two unique: xorAll has bits where they differ; use lowest set bit to split into two groups, XOR each.
Java Template
// 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; }
๐ข
Bit Counting
Kernighan's trick: n &= (n-1) removes lowest set bit
popcountPower of 2โผ
Key Tricks
- 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)
n &= (n-1) clears lowest set bit. Repeat until 0 โ count iterations = popcount. Power of 2: exactly one bit set, so n>0 && (n&(n-1))==0.
Java Template
// 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);
๐
Bit Modulo (Single Number II)
Track bits appearing 1 mod 3 times using bit-wise accumulators
Mod 3Bitsโผ
Key Insight
- 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"
ones = bits seen 1 mod 3, twos = bits seen 2 mod 3. Update: ones = (ones^n) & ~twos; twos = (twos^n) & ~ones. After 3ร, both reset. Final ones = answer.
Java Template (Single Number II)
int ones = 0, twos = 0; for (int n : nums) { ones = (ones ^ n) & ~twos; twos = (twos ^ n) & ~ones; } return ones; // bit remaining after mod-3
๐ Type Conversion
๐ข Constants
๐ Math
๐ก Character
๐ String/SB
๐ฆ Arrays
๐ Collections
๐ Map/Set
โ PQ & Comparator
๐ Deque
โก Bit Ops
๐ฒ Grid Utils
๐งฑ DSA Blocks
๐ Sorting
๐ฒ TreeMap
๐ Init Patterns
๐
Type Conversions & Parsing
intโString, charโint, arraysโlists
๐ขint โ StringParsing
// 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 โ int indexVery Common
// 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
๐กchar[] / String conversionsArrayโString
// 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"
๐Sorting / reversing primitivesCommon Gotcha
// 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 / Long / Infinity Constants
Bounds, overflow guards, safe infinity values
๐Integer & Long boundsMust Know
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
๐งฎMidpoint without overflowBinary Search
// โ 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;
โพ๏ธArrays.fill with sentinelDP Init
// 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 Class
java.lang.Math โ no import needed
๐min / max / abs / pow / sqrt
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);
๐log / floor / ceil / round
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 Class
java.lang.Character โ type checks, case conversion
๐Type checks
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
๐คCase conversion & bit tricks
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
๐
String & StringBuilder
Immutable String API + mutable StringBuilder
๐String core methods
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+)
๐String transform methods
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+)
๐ขString comparison & equality
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 โ mutable string
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 Class
java.util.Arrays โ sort, fill, copy, search
๐Sort & Fill
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]);
๐Copy & Equality
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 & Stream
// 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 Class
java.util.Collections โ utility methods for List/Set/Map
๐Sort, Reverse, Shuffle
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());
๐Min, Max, Frequency
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
๐Factory & Wrapper methods
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);
๐
HashMap / HashSet Tricks
O(1) lookup, frequency counting, grouping
๐Core HashMap operations
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>
๐ขFrequency counting patternsVery Common
// 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);
๐HashSet operations
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); ... }
โ
Priority Queue & Comparator
Min-heap, max-heap, custom ordering, lambda comparators
๐Min-heap / Max-heap
// 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()
๐งCustom comparatorsLambda
// 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)
๐Comparator for Arrays.sort & List.sort
// 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 โ Stack, Queue, Sliding Window
ArrayDeque is faster than Stack and LinkedList
๐ฅDeque as Stack (LIFO)
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 as Queue (FIFO)
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 both ends (Monotonic Window)
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
โก
Bit Operations Cheatsheet
Check, set, clear, toggle bits โ integer-level tricks
๐ฌBit read / write operationsMust Know
// 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);
๐ขLowest set bit & countingKernighan
// 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);
โกShift & arithmetic tricks
// 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 properties & bitmask
// 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)
๐ฒ
Grid / Matrix Utilities
Direction arrays, bounds check, index conversion, traversals
๐งญDirection arraysBFS/DFS on Grid
// 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 }
๐Index conversion & transforms
// 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
๐Matrix traversal patterns
// 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); }
๐งฑ
DSA Building Blocks
GCD, pivot, palindrome, modular exp, union-find, node classes
๐ขGCD, LCM, Prime check
// 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
๐Find pivot in rotated sorted array
// 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
๐Palindrome check
// 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 }
๐Node class templatesCopy-paste
// 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; } }
โกModular arithmetic & fast power
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)
๐Compact Union-FindCopy-paste
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; }
๐
Sorting Comparator Patterns
Lambda comparators for every scenario
๐คSort strings various ways
// 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);
๐Sort 2D arrays / objects
// 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());
โ ๏ธComparator pitfalls
// โ 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, TreeSet, LinkedHashMap
Sorted keys, floor/ceiling, insertion-order map
๐TreeMap โ sorted keys
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 & LinkedHashMap
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
๐
Common Initialization Patterns
Frequency arrays, adjacency list, DP tables, visited arrays
๐Frequency arraysString Problems
// 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)
๐ธGraph adjacency list
// 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]]++;
๐ฏDP table initializations
// 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
๐Common result variable patterns
// 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 parsing edge cases
// 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
๐ฅ Overflow
๐ญ Empty/Null
1๏ธโฃ Single/Two Elem
๐ฆ Arrays
๐ช Sliding Window
๐ Binary Search
๐ Linked List
๐ณ Tree
๐ธ Graph
๐ Stack/Queue
๐ฏ DP
๐ฟ Backtracking
๐ Strings
โก Bit Manip
โ ๏ธ General
๐ฅ
Integer Overflow
Silent wrapping is one of the most common hidden bugs
โAddition overflowSilent Bug
Bug:
Fix: Cast to long before adding.
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
โ๏ธMultiplication overflowSilent Bug
Bug:
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
โ๏ธBinary search midpoint overflowClassic
Bug:
(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 negationOverflow Trap
Bug:
-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
โพ๏ธINF + cost overflows in Dijkstra/DPSubtle
Bug:
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)
๐ญ
Empty / Null Input
Always handle before any index access or loop
๐ญEmpty / null arrayAIOOBE / NPE
Bug:
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
๐คEmpty / null stringNPE / SIOOBE
Bug:
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
๐Null head (Linked List)NPE
Bug:
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 root (Tree)NPE
Every recursive tree function must handle
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);
1๏ธโฃ
Single / Two Elements
Most algorithms silently fail for n=1 or n=2
1๏ธโฃSingle-element arrayOff-by-one
Two pointers converge immediately. Kadane's loop never runs. Binary search returns immediately. All generally correct โ verify return value.
// 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];
2๏ธโฃTwo-element casesHead removal
Remove Nth from end of 2-node list. Middle of 2-node list. House Robber II circular with 2 elements.
// 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 โ
๐ณSingle-node treeCheck
Root with no children. The root IS both a leaf and the only node. Diameter=0, height=0 (by common definition).
// 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
๐ฆ
Array-Specific Edge Cases
All-same, all-negative, zeros, sorted/reverse, duplicates
๐ขAll elements sameDuplicate handling
Watch: Rotated array search, 3Sum dedup, LCS, partition all need explicit duplicate handling.
// 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
โAll negative numbersWrong init
Bug: Initializing answer to
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?
0๏ธโฃZeros in arrayProduct / Reset
Zeros reset product subarrays. Two zeros in Product Except Self means all positions are 0.
// 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
๐Already sorted / not rotatedRotation=0
Case: Rotated sorted array that isn't actually rotated โ pivot index = 0. Must still work.
// [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
๐ช
Sliding Window Edge Cases
k > n, all match, no match, matches counter boundary
๐Window size k > nAIOOBE
Bug: Building first window of size k when k > n causes index out of bounds.
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;
โ
All chars / no chars satisfy conditionResult init
Case: Min Window โ no valid window should return
"", 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 counter โ boundary crossSubtle
Pattern: Increment/decrement
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
๐
Binary Search Edge Cases
Infinite loop, off-by-one, wrong boundary, duplicate handling
๐Infinite loop: lo = midHangs forever
Bug: When
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 / last occurrence of duplicateDon't return early
Issue: For first/last occurrence, don't return when found โ keep narrowing in the correct direction.
// 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)
๐ฏSearch-on-Answer wrong boundariesWrong Answer
Issue: lo/hi must cover the complete valid range. Setting lo too high or hi too low misses the answer.
// 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!
๐
Linked List Edge Cases
NPE order, lost pointer, even/odd length, head removal
๐ขFast/slow null check orderNPE
Bug: Checking
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) โ
๐Reverse: save next before overwritingLost Pointer
Bug: Writing
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
๐Remove Nth: n equals list lengthHead deletion
Case: n = list length means remove the head. Without a dummy node this requires a special case.
// โ 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 โ
๐Cycle: phase 2 detectionTwo phases
Floyd's cycle detection is two phases. Phase 1 detects a cycle. Phase 2 finds the entry point โ reset one pointer to head.
// 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
๐ณ
Tree Edge Cases
Skewed, negative values, duplicate keys, LCA ancestor case
๐Skewed tree โ stack overflown=10โต depth
Risk: A fully skewed tree (all left or all right) has depth = n. Recursive DFS โ n stack frames โ StackOverflowError.
// 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 โ
๐ขBST int bounds vs long boundsWrong validation
Bug: Node value =
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);
โNegative node values in path/sumWrong init
Bug: Max Path Sum initialized to 0 gives wrong answer when all nodes are negative.
// โ 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
๐ฟLCA โ p is ancestor of qEarly return
Case: One node is the direct ancestor of the other. The ancestor is the LCA. Standard algorithm handles this correctly.
// 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
๐ธ
Graph Edge Cases
Disconnected components, self-loops, negative weights, direction
๐Disconnected graph โ incomplete BFSMissed nodes
Bug: Starting BFS/DFS from a single source won't visit nodes in other connected components.
// โ 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)
๐บDijkstra: stale entries + negative weightsWrong Answer
Bug 1: Without stale-entry check, outdated PQ entries process nodes with suboptimal distances.
Bug 2: Dijkstra is incorrect with negative edges โ use Bellman-Ford.
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
๐Undirected: parent โ back-edgeFalse cycle
Issue: In undirected DFS, the edge back to the parent node looks like a back-edge (cycle) but isn't.
// โ 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 / Queue Edge Cases
Pop on empty, leftover elements, calculator sign edge cases
๐ญPop / peek on empty stackException
Bug:
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
๐Leftover elements after loopMonotonic Stack
Issue: After processing all elements, items remaining in a monotonic stack have no next greater/smaller element.
// 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 Edge Cases
Base case index, circular input, loop direction, empty result
0๏ธโฃWrong base case / index-0 crashAIOOBE
Bug: Accessing
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!
๐Circular array: run DP twiceHouse Robber II
Pattern: Can't use first and last element together. Run 1D DP twice on [0..n-2] and [1..n-1], take max.
// [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 vs 0/1 knapsack loop directionReuse bug
Key: Forward loop allows reuse (unbounded). Backward loop prevents reuse (0/1). Swapping them gives wrong answers.
// 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
๐ฟ
Backtracking Edge Cases
Snapshot, duplicate skip level, pruning order
๐ธMust snapshot the listShared reference
Bug: Adding
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
๐ขDuplicate skip: same depth, not globali > start
Issue:
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
๐
String Edge Cases
== vs equals, empty split, palindrome definition
โ๏ธ== vs .equals() for stringsWrong compare
Bug:
== 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 โ
๐Palindrome: empty / all-same / spacesDefinition
Clarify: Does the problem use alphanumeric only? Case-insensitive? Single char is always a palindrome. Empty string is a palindrome.
// "" (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
โ๏ธ"".split() returns length=1, not 0Split trap
Bug: Splitting an empty string doesn't return an empty array โ it returns
[""] (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",""]
โก
Bit Manipulation Edge Cases
>> vs >>>, n=0, Integer.MIN_VALUE, power-of-2 check
โก๏ธ>> vs >>> for negative numbersSign propagation
Issue:
>> 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๏ธโฃn=0 and power-of-2 checkMissing guard
Bug:
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
โ ๏ธ
Universal Edge Cases
Off-by-one, modulo negatives, comparator overflow, return values
1๏ธโฃOff-by-one errorsMost Common
Most frequent bug. Wrong loop bound, wrong index, wrong substring range.
// 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
โNegative modulo in JavaLanguage trap
Bug: Java's
% 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)
๐Comparator: subtraction overflowIllegalArgument
Bug:
(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
๐What to return when no answer existsRead problem
Always check: Different problems have different "not found" return values. Don't assume.
// 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];
๐Early-return checklist templateBest Practice
Habit: Write these guards at the very top of every solution before any logic.
// 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;
๐ConcurrentModificationExceptionRuntime
Bug: Adding or removing from a collection inside a for-each loop over that same collection.
// โ 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
๐ 1,000+ students enrolled
๐ท๏ธ 90% OFF โ Limited Time!
- ๐ฌ 170+ animated visualizations
- ๐ All 17 topics covered
- ๐งฉ Patterns, Tricks & Edge Cases
- โพ๏ธ Lifetime updates included
Monthly
1 Month
โญ Best Value
Quarterly
3 Months
๐ฅ Popular
Annual
1 Year
๐ท๏ธ Have a promo code?
๐ Secured by Razorpay ยท UPI, Cards, Netbanking accepted