Skip to content

Patterns Quick Reference Guide

Table of Contents

  1. Two Pointers
  2. Fast & Slow Pointers
  3. Sliding Window
  4. Intervals
  5. Stack
  6. Linked List
  7. Binary Search
  8. Heap / Priority Queue
  9. DFS (Depth-First Search)
  10. BFS (Breadth-First Search)
  11. Backtracking
  12. Monotonic Stack
  13. Greedy
  14. Bit Manipulation
  15. Prefix Sum
  16. Matrix Manipulation
  17. Union Find (Disjoint Set)
  18. Graphs
  19. Dynamic Programming
  20. Trie (Prefix Tree)
  21. Topological Sort
  22. Segment Tree
  23. Binary Indexed Tree (Fenwick Tree)
  24. Two Heaps
  25. Cyclic Sort

1. Two Pointers

Description

Uses two pointers to iterate through data structures, typically moving toward each other or in the same direction. Reduces time complexity from O(n²) to O(n).

When to Use

  • Sorted arrays/lists
  • Finding pairs with a specific sum/condition
  • Removing duplicates in-place
  • Partitioning arrays
  • Palindrome verification
  • Container/area problems

Example Problems

Problem Difficulty Key Insight
Two Sum II (Sorted) Easy Start from both ends
3Sum Medium Fix one, two-pointer on rest
Container With Most Water Medium Move pointer with smaller height
Trapping Rain Water Hard Track left/right max
Remove Duplicates from Sorted Array Easy Slow/fast pointer
Sort Colors (Dutch National Flag) Medium Three pointers

Java Code Snippets

Two Sum II (Sorted Array)

public int[] twoSum(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left < right) {
        int sum = nums[left] + nums[right];
        if (sum == target) {
            return new int[]{left + 1, right + 1};
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return new int[]{-1, -1};
}

3Sum

public List<List<Integer>> threeSum(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    Arrays.sort(nums);

    for (int i = 0; i < nums.length - 2; i++) {
        if (i > 0 && nums[i] == nums[i - 1]) continue; // Skip duplicates

        int left = i + 1, right = nums.length - 1;
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            if (sum == 0) {
                result.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--;
            }
        }
    }
    return result;
}

Container With Most Water

public int maxArea(int[] height) {
    int left = 0, right = height.length - 1;
    int maxArea = 0;

    while (left < right) {
        int width = right - left;
        int h = Math.min(height[left], height[right]);
        maxArea = Math.max(maxArea, width * h);

        if (height[left] < height[right]) {
            left++;
        } else {
            right--;
        }
    }
    return maxArea;
}

Trapping Rain Water

public int trap(int[] height) {
    int left = 0, right = height.length - 1;
    int leftMax = 0, rightMax = 0;
    int water = 0;

    while (left < right) {
        if (height[left] < height[right]) {
            if (height[left] >= leftMax) {
                leftMax = height[left];
            } else {
                water += leftMax - height[left];
            }
            left++;
        } else {
            if (height[right] >= rightMax) {
                rightMax = height[right];
            } else {
                water += rightMax - height[right];
            }
            right--;
        }
    }
    return water;
}

2. Fast & Slow Pointers

Description

Also known as "Floyd's Tortoise and Hare" algorithm. Uses two pointers moving at different speeds to detect cycles or find middle elements.

When to Use

  • Cycle detection in linked lists or arrays
  • Finding the middle of a linked list
  • Finding the start of a cycle
  • Palindrome linked list verification
  • Happy number problem

Example Problems

Problem Difficulty Key Insight
Linked List Cycle Easy Fast catches slow if cycle
Linked List Cycle II Medium Reset one to head after meeting
Find the Duplicate Number Medium Array as linked list
Middle of the Linked List Easy Fast moves 2x
Happy Number Easy Number sequence forms cycle
Palindrome Linked List Easy Find middle, reverse, compare

Java Code Snippets

Linked List Cycle Detection

public boolean hasCycle(ListNode head) {
    if (head == null || head.next == null) return false;

    ListNode slow = head, fast = head;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
        if (slow == fast) return true;
    }
    return false;
}

Find Cycle Start (Linked List Cycle II)

public ListNode detectCycle(ListNode head) {
    if (head == null || head.next == null) return null;

    ListNode slow = head, fast = head;

    // Detect cycle
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
        if (slow == fast) break;
    }

    if (fast == null || fast.next == null) return null;

    // Find cycle start
    slow = head;
    while (slow != fast) {
        slow = slow.next;
        fast = fast.next;
    }
    return slow;
}

Find the Duplicate Number

public int findDuplicate(int[] nums) {
    int slow = nums[0], fast = nums[0];

    // Find intersection point
    do {
        slow = nums[slow];
        fast = nums[nums[fast]];
    } while (slow != fast);

    // Find entrance to cycle
    slow = nums[0];
    while (slow != fast) {
        slow = nums[slow];
        fast = nums[fast];
    }
    return slow;
}

Middle of Linked List

public ListNode middleNode(ListNode head) {
    ListNode slow = head, fast = head;
    while (fast != null && fast.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;
}

3. Sliding Window

Description

Maintains a "window" over a contiguous sequence of elements, sliding it across the data structure. Converts O(n*k) problems to O(n).

When to Use

  • Subarray/substring problems with constraints
  • Finding max/min in subarrays of size k
  • Longest/shortest substring with conditions
  • Anagram finding
  • Fixed or variable window size problems

Example Problems

Problem Difficulty Key Insight
Maximum Sum Subarray of Size K Easy Fixed window
Longest Substring Without Repeating Medium Variable window + HashSet
Minimum Window Substring Hard Variable window + frequency map
Longest Repeating Character Replacement Medium Track max frequency
Permutation in String Medium Fixed window + frequency match
Sliding Window Maximum Hard Monotonic deque

Java Code Snippets

Fixed Window - Maximum Sum Subarray

public int maxSumSubarray(int[] nums, int k) {
    int windowSum = 0, maxSum = 0;

    for (int i = 0; i < nums.length; i++) {
        windowSum += nums[i];

        if (i >= k - 1) {
            maxSum = Math.max(maxSum, windowSum);
            windowSum -= nums[i - k + 1];
        }
    }
    return maxSum;
}

Longest Substring Without Repeating Characters

public int lengthOfLongestSubstring(String s) {
    Map<Character, Integer> map = new HashMap<>();
    int maxLen = 0, left = 0;

    for (int right = 0; right < s.length(); right++) {
        char c = s.charAt(right);
        if (map.containsKey(c)) {
            left = Math.max(left, map.get(c) + 1);
        }
        map.put(c, right);
        maxLen = Math.max(maxLen, right - left + 1);
    }
    return maxLen;
}

Minimum Window Substring

public String minWindow(String s, String t) {
    if (s.length() < t.length()) return "";

    Map<Character, Integer> need = new HashMap<>();
    for (char c : t.toCharArray()) {
        need.put(c, need.getOrDefault(c, 0) + 1);
    }

    int left = 0, minLen = Integer.MAX_VALUE, minStart = 0;
    int required = need.size(), formed = 0;
    Map<Character, Integer> window = new HashMap<>();

    for (int right = 0; right < s.length(); right++) {
        char c = s.charAt(right);
        window.put(c, window.getOrDefault(c, 0) + 1);

        if (need.containsKey(c) && window.get(c).intValue() == need.get(c).intValue()) {
            formed++;
        }

        while (formed == required) {
            if (right - left + 1 < minLen) {
                minLen = right - left + 1;
                minStart = left;
            }

            char leftChar = s.charAt(left);
            window.put(leftChar, window.get(leftChar) - 1);
            if (need.containsKey(leftChar) && window.get(leftChar) < need.get(leftChar)) {
                formed--;
            }
            left++;
        }
    }
    return minLen == Integer.MAX_VALUE ? "" : s.substring(minStart, minStart + minLen);
}

Longest Repeating Character Replacement

public int characterReplacement(String s, int k) {
    int[] count = new int[26];
    int maxCount = 0, maxLen = 0, left = 0;

    for (int right = 0; right < s.length(); right++) {
        count[s.charAt(right) - 'A']++;
        maxCount = Math.max(maxCount, count[s.charAt(right) - 'A']);

        // Window size - max frequency > k means we need to shrink
        while (right - left + 1 - maxCount > k) {
            count[s.charAt(left) - 'A']--;
            left++;
        }
        maxLen = Math.max(maxLen, right - left + 1);
    }
    return maxLen;
}

4. Intervals

Description

Deals with problems involving intervals (start, end pairs). Often requires sorting and merging/comparing intervals.

When to Use

  • Merging overlapping intervals
  • Finding intersections
  • Meeting room scheduling
  • Inserting intervals
  • Interval coverage problems

Example Problems

Problem Difficulty Key Insight
Merge Intervals Medium Sort by start, merge overlaps
Insert Interval Medium Find position, merge
Non-overlapping Intervals Medium Greedy, sort by end
Meeting Rooms Easy Sort, check overlap
Meeting Rooms II Medium Min heap or two pointers
Interval List Intersections Medium Two pointers

Java Code Snippets

Merge Intervals

public int[][] merge(int[][] intervals) {
    if (intervals.length <= 1) return intervals;

    Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
    List<int[]> result = new ArrayList<>();
    int[] current = intervals[0];
    result.add(current);

    for (int[] interval : intervals) {
        if (interval[0] <= current[1]) {
            current[1] = Math.max(current[1], interval[1]);
        } else {
            current = interval;
            result.add(current);
        }
    }
    return result.toArray(new int[result.size()][]);
}

Meeting Rooms II (Minimum Meeting Rooms)

public int minMeetingRooms(int[][] intervals) {
    if (intervals.length == 0) return 0;

    int[] starts = new int[intervals.length];
    int[] ends = new int[intervals.length];

    for (int i = 0; i < intervals.length; i++) {
        starts[i] = intervals[i][0];
        ends[i] = intervals[i][1];
    }

    Arrays.sort(starts);
    Arrays.sort(ends);

    int rooms = 0, endPtr = 0;
    for (int i = 0; i < starts.length; i++) {
        if (starts[i] < ends[endPtr]) {
            rooms++;
        } else {
            endPtr++;
        }
    }
    return rooms;
}

// Alternative: Min Heap approach
public int minMeetingRoomsHeap(int[][] intervals) {
    Arrays.sort(intervals, (a, b) -> a[0] - b[0]);
    PriorityQueue<Integer> heap = new PriorityQueue<>();

    for (int[] interval : intervals) {
        if (!heap.isEmpty() && heap.peek() <= interval[0]) {
            heap.poll();
        }
        heap.offer(interval[1]);
    }
    return heap.size();
}

Non-overlapping Intervals (Minimum Removals)

public int eraseOverlapIntervals(int[][] intervals) {
    if (intervals.length == 0) return 0;

    // Sort by end time (greedy)
    Arrays.sort(intervals, (a, b) -> a[1] - b[1]);

    int count = 0, prevEnd = Integer.MIN_VALUE;
    for (int[] interval : intervals) {
        if (interval[0] >= prevEnd) {
            prevEnd = interval[1];
        } else {
            count++; // Remove this interval
        }
    }
    return count;
}

Insert Interval

public int[][] insert(int[][] intervals, int[] newInterval) {
    List<int[]> result = new ArrayList<>();
    int i = 0, n = intervals.length;

    // Add all intervals before newInterval
    while (i < n && intervals[i][1] < newInterval[0]) {
        result.add(intervals[i++]);
    }

    // Merge overlapping intervals
    while (i < n && intervals[i][0] <= newInterval[1]) {
        newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
        newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
        i++;
    }
    result.add(newInterval);

    // Add remaining intervals
    while (i < n) {
        result.add(intervals[i++]);
    }

    return result.toArray(new int[result.size()][]);
}

5. Stack

Description

LIFO (Last-In-First-Out) data structure. Essential for parsing, matching, and maintaining order of elements.

When to Use

  • Parentheses/brackets matching
  • Expression evaluation
  • Next greater/smaller element
  • Undo operations
  • DFS implementation
  • Simplifying paths

Example Problems

Problem Difficulty Key Insight
Valid Parentheses Easy Push open, pop close
Evaluate Reverse Polish Notation Medium Stack for operands
Daily Temperatures Medium Monotonic decreasing stack
Decode String Medium Stack for nested structures
Basic Calculator Hard Stack for precedence
Largest Rectangle in Histogram Hard Monotonic stack

Java Code Snippets

Valid Parentheses

public boolean isValid(String s) {
    Stack<Character> stack = new Stack<>();
    Map<Character, Character> map = Map.of(')', '(', '}', '{', ']', '[');

    for (char c : s.toCharArray()) {
        if (map.containsKey(c)) {
            if (stack.isEmpty() || stack.pop() != map.get(c)) {
                return false;
            }
        } else {
            stack.push(c);
        }
    }
    return stack.isEmpty();
}

Evaluate Reverse Polish Notation

public int evalRPN(String[] tokens) {
    Stack<Integer> stack = new Stack<>();

    for (String token : tokens) {
        if ("+-*/".contains(token)) {
            int b = stack.pop(), a = stack.pop();
            switch (token) {
                case "+": stack.push(a + b); break;
                case "-": stack.push(a - b); break;
                case "*": stack.push(a * b); break;
                case "/": stack.push(a / b); break;
            }
        } else {
            stack.push(Integer.parseInt(token));
        }
    }
    return stack.pop();
}

Decode String

public String decodeString(String s) {
    Stack<Integer> countStack = new Stack<>();
    Stack<StringBuilder> stringStack = new Stack<>();
    StringBuilder current = new StringBuilder();
    int k = 0;

    for (char c : s.toCharArray()) {
        if (Character.isDigit(c)) {
            k = k * 10 + (c - '0');
        } else if (c == '[') {
            countStack.push(k);
            stringStack.push(current);
            current = new StringBuilder();
            k = 0;
        } else if (c == ']') {
            StringBuilder decoded = stringStack.pop();
            int count = countStack.pop();
            while (count-- > 0) {
                decoded.append(current);
            }
            current = decoded;
        } else {
            current.append(c);
        }
    }
    return current.toString();
}

Daily Temperatures (Next Greater Element)

public int[] dailyTemperatures(int[] temperatures) {
    int n = temperatures.length;
    int[] result = new int[n];
    Stack<Integer> stack = new Stack<>(); // Store indices

    for (int i = 0; i < n; i++) {
        while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
            int idx = stack.pop();
            result[idx] = i - idx;
        }
        stack.push(i);
    }
    return result;
}

6. Linked List

Description

Linear data structure with nodes connected via pointers. Essential for understanding memory and pointer manipulation.

When to Use

  • In-place reversal
  • Merging sorted lists
  • Finding cycles
  • Removing nth node
  • Reordering lists
  • Adding numbers represented as lists

Example Problems

Problem Difficulty Key Insight
Reverse Linked List Easy Three pointers
Merge Two Sorted Lists Easy Dummy head
Remove Nth Node From End Medium Two pointers, n apart
Reorder List Medium Find middle, reverse, merge
Add Two Numbers Medium Carry handling
LRU Cache Medium HashMap + Doubly Linked List

Java Code Snippets

Reverse Linked List (Iterative)

public ListNode reverseList(ListNode head) {
    ListNode prev = null, curr = head;

    while (curr != null) {
        ListNode next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}

Reverse Linked List (Recursive)

public ListNode reverseListRecursive(ListNode head) {
    if (head == null || head.next == null) return head;

    ListNode newHead = reverseListRecursive(head.next);
    head.next.next = head;
    head.next = null;
    return newHead;
}

Merge Two Sorted Lists

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    ListNode dummy = new ListNode(0);
    ListNode curr = dummy;

    while (l1 != null && l2 != null) {
        if (l1.val <= l2.val) {
            curr.next = l1;
            l1 = l1.next;
        } else {
            curr.next = l2;
            l2 = l2.next;
        }
        curr = curr.next;
    }
    curr.next = (l1 != null) ? l1 : l2;
    return dummy.next;
}

Remove Nth Node From End

public ListNode removeNthFromEnd(ListNode head, int n) {
    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;
    return dummy.next;
}

LRU Cache

class LRUCache {
    class Node {
        int key, val;
        Node prev, next;
        Node(int k, int v) { key = k; val = v; }
    }

    private int capacity;
    private Map<Integer, Node> map;
    private Node head, tail;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>();
        head = new Node(0, 0);
        tail = new Node(0, 0);
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        if (!map.containsKey(key)) return -1;
        Node node = map.get(key);
        remove(node);
        insertAtHead(node);
        return node.val;
    }

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            remove(map.get(key));
        }
        Node node = new Node(key, value);
        map.put(key, node);
        insertAtHead(node);

        if (map.size() > capacity) {
            Node lru = tail.prev;
            remove(lru);
            map.remove(lru.key);
        }
    }

    private void remove(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    private void insertAtHead(Node node) {
        node.next = head.next;
        node.prev = head;
        head.next.prev = node;
        head.next = node;
    }
}

Description

Divide-and-conquer algorithm for searching in sorted arrays. Reduces time complexity from O(n) to O(log n).

When to Use

  • Sorted arrays/lists
  • Finding boundaries (first/last occurrence)
  • Search space problems
  • Rotated sorted arrays
  • Finding peak elements
  • Minimizing/maximizing answers

Example Problems

Problem Difficulty Key Insight
Binary Search Easy Classic template
First Bad Version Easy Left boundary
Search in Rotated Sorted Array Medium Find pivot or compare
Find Peak Element Medium Gradient ascent
Koko Eating Bananas Medium Binary search on answer
Median of Two Sorted Arrays Hard Binary search on partition

Java Code Snippets

public int search(int[] nums, int target) {
    int left = 0, right = nums.length - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}

Find First/Last Position (Left/Right Boundary)

public int[] searchRange(int[] nums, int target) {
    return new int[]{findFirst(nums, target), findLast(nums, target)};
}

private int findFirst(int[] nums, int target) {
    int left = 0, right = nums.length - 1, result = -1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            result = mid;
            right = mid - 1; // Continue searching left
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return result;
}

private int findLast(int[] nums, int target) {
    int left = 0, right = nums.length - 1, result = -1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            result = mid;
            left = mid + 1; // Continue searching right
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return result;
}

Search in Rotated Sorted Array

public int searchRotated(int[] nums, int target) {
    int left = 0, right = nums.length - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) return mid;

        // Left half is sorted
        if (nums[left] <= nums[mid]) {
            if (target >= nums[left] && target < nums[mid]) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        // Right half is sorted
        else {
            if (target > nums[mid] && target <= nums[right]) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
    }
    return -1;
}

Binary Search on Answer (Koko Eating Bananas)

public int minEatingSpeed(int[] piles, int h) {
    int left = 1, right = Arrays.stream(piles).max().getAsInt();

    while (left < right) {
        int mid = left + (right - left) / 2;
        if (canFinish(piles, mid, h)) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    return left;
}

private boolean canFinish(int[] piles, int speed, int h) {
    int hours = 0;
    for (int pile : piles) {
        hours += (pile + speed - 1) / speed; // Ceiling division
    }
    return hours <= h;
}

8. Heap / Priority Queue

Description

Tree-based data structure that maintains max/min at root. Supports O(log n) insertion and extraction.

When to Use

  • Finding k largest/smallest elements
  • Merge k sorted lists/arrays
  • Continuous median
  • Task scheduling
  • Dijkstra's algorithm
  • Huffman coding

Example Problems

Problem Difficulty Key Insight
Kth Largest Element Medium Min heap of size k
Top K Frequent Elements Medium Min heap by frequency
Merge K Sorted Lists Hard Min heap of list heads
Find Median from Data Stream Hard Two heaps
Task Scheduler Medium Max heap + cooldown
Reorganize String Medium Max heap for alternating

Java Code Snippets

Kth Largest Element

public int findKthLargest(int[] nums, int k) {
    PriorityQueue<Integer> minHeap = new PriorityQueue<>();

    for (int num : nums) {
        minHeap.offer(num);
        if (minHeap.size() > k) {
            minHeap.poll();
        }
    }
    return minHeap.peek();
}

Top K Frequent Elements

public int[] topKFrequent(int[] nums, int k) {
    Map<Integer, Integer> freq = new HashMap<>();
    for (int num : nums) {
        freq.put(num, freq.getOrDefault(num, 0) + 1);
    }

    // Min heap by frequency
    PriorityQueue<Integer> heap = new PriorityQueue<>(
        (a, b) -> freq.get(a) - freq.get(b)
    );

    for (int num : freq.keySet()) {
        heap.offer(num);
        if (heap.size() > k) {
            heap.poll();
        }
    }

    int[] result = new int[k];
    for (int i = k - 1; i >= 0; i--) {
        result[i] = heap.poll();
    }
    return result;
}

Merge K Sorted Lists

public ListNode mergeKLists(ListNode[] lists) {
    PriorityQueue<ListNode> heap = new PriorityQueue<>(
        (a, b) -> a.val - b.val
    );

    for (ListNode list : lists) {
        if (list != null) heap.offer(list);
    }

    ListNode dummy = new ListNode(0);
    ListNode curr = dummy;

    while (!heap.isEmpty()) {
        ListNode node = heap.poll();
        curr.next = node;
        curr = curr.next;
        if (node.next != null) {
            heap.offer(node.next);
        }
    }
    return dummy.next;
}

Task Scheduler

public int leastInterval(char[] tasks, int n) {
    int[] freq = new int[26];
    for (char task : tasks) {
        freq[task - 'A']++;
    }

    PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
    for (int f : freq) {
        if (f > 0) maxHeap.offer(f);
    }

    int cycles = 0;
    while (!maxHeap.isEmpty()) {
        List<Integer> temp = new ArrayList<>();
        for (int i = 0; i <= n; i++) {
            if (!maxHeap.isEmpty()) {
                temp.add(maxHeap.poll());
            }
        }

        for (int count : temp) {
            if (--count > 0) {
                maxHeap.offer(count);
            }
        }

        cycles += maxHeap.isEmpty() ? temp.size() : n + 1;
    }
    return cycles;
}

Description

Explores as far as possible along each branch before backtracking. Uses recursion or explicit stack.

When to Use

  • Tree/graph traversal
  • Path finding
  • Connected components
  • Cycle detection
  • Topological sorting
  • Island problems
  • Generating permutations/combinations

Example Problems

Problem Difficulty Key Insight
Number of Islands Medium DFS to mark visited
Clone Graph Medium HashMap for visited
Path Sum Easy Track remaining sum
Binary Tree Maximum Path Sum Hard Track max through node
Course Schedule Medium Cycle detection
Word Search Medium Backtracking DFS

Java Code Snippets

Number of Islands

public int numIslands(char[][] grid) {
    if (grid == null || grid.length == 0) return 0;

    int count = 0;
    for (int i = 0; i < grid.length; i++) {
        for (int j = 0; j < grid[0].length; j++) {
            if (grid[i][j] == '1') {
                dfs(grid, i, j);
                count++;
            }
        }
    }
    return count;
}

private void dfs(char[][] grid, int i, int j) {
    if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length
        || grid[i][j] != '1') {
        return;
    }

    grid[i][j] = '0'; // Mark visited
    dfs(grid, i + 1, j);
    dfs(grid, i - 1, j);
    dfs(grid, i, j + 1);
    dfs(grid, i, j - 1);
}

Clone Graph

public Node cloneGraph(Node node) {
    if (node == null) return null;

    Map<Node, Node> visited = new HashMap<>();
    return dfsClone(node, visited);
}

private Node dfsClone(Node node, Map<Node, Node> visited) {
    if (visited.containsKey(node)) {
        return visited.get(node);
    }

    Node clone = new Node(node.val);
    visited.put(node, clone);

    for (Node neighbor : node.neighbors) {
        clone.neighbors.add(dfsClone(neighbor, visited));
    }
    return clone;
}

Binary Tree Maximum Path Sum

private int maxSum = Integer.MIN_VALUE;

public int maxPathSum(TreeNode root) {
    maxGain(root);
    return maxSum;
}

private int maxGain(TreeNode node) {
    if (node == null) return 0;

    int leftGain = Math.max(maxGain(node.left), 0);
    int rightGain = Math.max(maxGain(node.right), 0);

    // Path through current node
    int pathSum = node.val + leftGain + rightGain;
    maxSum = Math.max(maxSum, pathSum);

    // Return max gain if continuing path
    return node.val + Math.max(leftGain, rightGain);
}
public boolean exist(char[][] board, String word) {
    for (int i = 0; i < board.length; i++) {
        for (int j = 0; j < board[0].length; j++) {
            if (dfs(board, word, i, j, 0)) {
                return true;
            }
        }
    }
    return false;
}

private boolean dfs(char[][] board, String word, int i, int j, int idx) {
    if (idx == word.length()) return true;

    if (i < 0 || i >= board.length || j < 0 || j >= board[0].length
        || board[i][j] != word.charAt(idx)) {
        return false;
    }

    char temp = board[i][j];
    board[i][j] = '#'; // Mark visited

    boolean found = dfs(board, word, i + 1, j, idx + 1)
                 || dfs(board, word, i - 1, j, idx + 1)
                 || dfs(board, word, i, j + 1, idx + 1)
                 || dfs(board, word, i, j - 1, idx + 1);

    board[i][j] = temp; // Restore
    return found;
}

Description

Explores all neighbors at current depth before moving to next level. Uses queue. Optimal for shortest path in unweighted graphs.

When to Use

  • Shortest path (unweighted)
  • Level-order traversal
  • Finding minimum steps
  • Multi-source BFS
  • Graph exploration by layers
  • Word ladder problems

Example Problems

Problem Difficulty Key Insight
Binary Tree Level Order Traversal Medium Queue + level size
Rotting Oranges Medium Multi-source BFS
Word Ladder Hard BFS for shortest transformation
Shortest Path in Binary Matrix Medium BFS with 8 directions
Open the Lock Medium State space BFS
Minimum Knight Moves Medium BFS on infinite board

Java Code Snippets

Level Order Traversal

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> result = new ArrayList<>();
    if (root == null) return result;

    Queue<TreeNode> queue = new LinkedList<>();
    queue.offer(root);

    while (!queue.isEmpty()) {
        int levelSize = queue.size();
        List<Integer> level = new ArrayList<>();

        for (int i = 0; i < levelSize; i++) {
            TreeNode node = queue.poll();
            level.add(node.val);
            if (node.left != null) queue.offer(node.left);
            if (node.right != null) queue.offer(node.right);
        }
        result.add(level);
    }
    return result;
}

Rotting Oranges (Multi-source BFS)

public int orangesRotting(int[][] grid) {
    int rows = grid.length, cols = grid[0].length;
    Queue<int[]> queue = new LinkedList<>();
    int fresh = 0;

    // Initialize: add all rotten oranges to queue
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            if (grid[i][j] == 2) {
                queue.offer(new int[]{i, j});
            } else if (grid[i][j] == 1) {
                fresh++;
            }
        }
    }

    if (fresh == 0) return 0;

    int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    int minutes = 0;

    while (!queue.isEmpty()) {
        int size = queue.size();
        for (int i = 0; i < size; i++) {
            int[] curr = queue.poll();
            for (int[] dir : dirs) {
                int ni = curr[0] + dir[0];
                int nj = curr[1] + dir[1];

                if (ni >= 0 && ni < rows && nj >= 0 && nj < cols
                    && grid[ni][nj] == 1) {
                    grid[ni][nj] = 2;
                    fresh--;
                    queue.offer(new int[]{ni, nj});
                }
            }
        }
        if (!queue.isEmpty()) minutes++;
    }

    return fresh == 0 ? minutes : -1;
}

Word Ladder

public int ladderLength(String beginWord, String endWord, List<String> wordList) {
    Set<String> wordSet = new HashSet<>(wordList);
    if (!wordSet.contains(endWord)) return 0;

    Queue<String> queue = new LinkedList<>();
    queue.offer(beginWord);
    Set<String> visited = new HashSet<>();
    visited.add(beginWord);
    int level = 1;

    while (!queue.isEmpty()) {
        int size = queue.size();
        for (int i = 0; i < size; i++) {
            String word = queue.poll();
            char[] chars = word.toCharArray();

            for (int j = 0; j < chars.length; j++) {
                char original = chars[j];
                for (char c = 'a'; c <= 'z'; c++) {
                    if (c == original) continue;
                    chars[j] = c;
                    String newWord = new String(chars);

                    if (newWord.equals(endWord)) return level + 1;

                    if (wordSet.contains(newWord) && !visited.contains(newWord)) {
                        visited.add(newWord);
                        queue.offer(newWord);
                    }
                }
                chars[j] = original;
            }
        }
        level++;
    }
    return 0;
}

Shortest Path in Binary Matrix

public int shortestPathBinaryMatrix(int[][] grid) {
    int n = grid.length;
    if (grid[0][0] == 1 || grid[n-1][n-1] == 1) return -1;

    int[][] dirs = {{0,1},{0,-1},{1,0},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1}};
    Queue<int[]> queue = new LinkedList<>();
    queue.offer(new int[]{0, 0});
    grid[0][0] = 1; // Mark visited
    int path = 1;

    while (!queue.isEmpty()) {
        int size = queue.size();
        for (int i = 0; i < size; i++) {
            int[] curr = queue.poll();
            if (curr[0] == n - 1 && curr[1] == n - 1) return path;

            for (int[] dir : dirs) {
                int ni = curr[0] + dir[0];
                int nj = curr[1] + dir[1];

                if (ni >= 0 && ni < n && nj >= 0 && nj < n && grid[ni][nj] == 0) {
                    grid[ni][nj] = 1;
                    queue.offer(new int[]{ni, nj});
                }
            }
        }
        path++;
    }
    return -1;
}

11. Backtracking

Description

Systematic way to explore all potential solutions by building candidates and abandoning them when they fail to satisfy constraints.

When to Use

  • Permutations and combinations
  • Subset generation
  • N-Queens problem
  • Sudoku solver
  • Path finding with constraints
  • Generating all valid configurations

Example Problems

Problem Difficulty Key Insight
Subsets Medium Include/exclude each element
Permutations Medium Swap and recurse
Combination Sum Medium Choices with duplicates allowed
N-Queens Hard Validate diagonal attacks
Sudoku Solver Hard Try each number, backtrack
Palindrome Partitioning Medium Validate + generate

Java Code Snippets

Subsets

public List<List<Integer>> subsets(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    backtrack(nums, 0, new ArrayList<>(), result);
    return result;
}

private void backtrack(int[] nums, int start, List<Integer> current,
                       List<List<Integer>> result) {
    result.add(new ArrayList<>(current));

    for (int i = start; i < nums.length; i++) {
        current.add(nums[i]);
        backtrack(nums, i + 1, current, result);
        current.remove(current.size() - 1);
    }
}

Permutations

public List<List<Integer>> permute(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    backtrackPermute(nums, new ArrayList<>(), new boolean[nums.length], result);
    return result;
}

private void backtrackPermute(int[] nums, List<Integer> current,
                               boolean[] used, List<List<Integer>> result) {
    if (current.size() == nums.length) {
        result.add(new ArrayList<>(current));
        return;
    }

    for (int i = 0; i < nums.length; i++) {
        if (used[i]) continue;

        used[i] = true;
        current.add(nums[i]);
        backtrackPermute(nums, current, used, result);
        current.remove(current.size() - 1);
        used[i] = false;
    }
}

Combination Sum

public List<List<Integer>> combinationSum(int[] candidates, int target) {
    List<List<Integer>> result = new ArrayList<>();
    backtrackCombSum(candidates, target, 0, new ArrayList<>(), result);
    return result;
}

private void backtrackCombSum(int[] candidates, int remain, int start,
                               List<Integer> current, List<List<Integer>> result) {
    if (remain == 0) {
        result.add(new ArrayList<>(current));
        return;
    }
    if (remain < 0) return;

    for (int i = start; i < candidates.length; i++) {
        current.add(candidates[i]);
        backtrackCombSum(candidates, remain - candidates[i], i, current, result);
        current.remove(current.size() - 1);
    }
}

N-Queens

public List<List<String>> solveNQueens(int n) {
    List<List<String>> result = new ArrayList<>();
    char[][] board = new char[n][n];
    for (char[] row : board) Arrays.fill(row, '.');

    backtrackQueens(board, 0, result);
    return result;
}

private void backtrackQueens(char[][] board, int row, List<List<String>> result) {
    if (row == board.length) {
        result.add(construct(board));
        return;
    }

    for (int col = 0; col < board.length; col++) {
        if (isValid(board, row, col)) {
            board[row][col] = 'Q';
            backtrackQueens(board, row + 1, result);
            board[row][col] = '.';
        }
    }
}

private boolean isValid(char[][] board, int row, int col) {
    // Check column
    for (int i = 0; i < row; i++) {
        if (board[i][col] == 'Q') return false;
    }
    // Check diagonal (upper left)
    for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
        if (board[i][j] == 'Q') return false;
    }
    // Check diagonal (upper right)
    for (int i = row - 1, j = col + 1; i >= 0 && j < board.length; i--, j++) {
        if (board[i][j] == 'Q') return false;
    }
    return true;
}

private List<String> construct(char[][] board) {
    List<String> result = new ArrayList<>();
    for (char[] row : board) {
        result.add(new String(row));
    }
    return result;
}

12. Monotonic Stack

Description

A stack that maintains elements in sorted (increasing or decreasing) order. Used to find next/previous greater/smaller elements efficiently.

When to Use

  • Next greater element problems
  • Stock span problems
  • Largest rectangle in histogram
  • Trapping rain water
  • Building with view of ocean
  • Temperature problems

Example Problems

Problem Difficulty Key Insight
Next Greater Element I Easy Map + monotonic stack
Daily Temperatures Medium Decreasing stack, store indices
Largest Rectangle in Histogram Hard Find boundaries
Maximal Rectangle Hard Build histogram per row
132 Pattern Medium Decreasing stack from right
Sum of Subarray Minimums Medium Contribution of each element

Java Code Snippets

Largest Rectangle in Histogram

public int largestRectangleArea(int[] heights) {
    Stack<Integer> stack = new Stack<>();
    int maxArea = 0;
    int n = heights.length;

    for (int i = 0; i <= n; i++) {
        int h = (i == n) ? 0 : heights[i];

        while (!stack.isEmpty() && h < heights[stack.peek()]) {
            int height = heights[stack.pop()];
            int width = stack.isEmpty() ? i : i - stack.peek() - 1;
            maxArea = Math.max(maxArea, height * width);
        }
        stack.push(i);
    }
    return maxArea;
}

Next Greater Element I

public int[] nextGreaterElement(int[] nums1, int[] nums2) {
    Map<Integer, Integer> nextGreater = new HashMap<>();
    Stack<Integer> stack = new Stack<>();

    // Build map of next greater elements for nums2
    for (int num : nums2) {
        while (!stack.isEmpty() && stack.peek() < num) {
            nextGreater.put(stack.pop(), num);
        }
        stack.push(num);
    }

    // Answer queries from nums1
    int[] result = new int[nums1.length];
    for (int i = 0; i < nums1.length; i++) {
        result[i] = nextGreater.getOrDefault(nums1[i], -1);
    }
    return result;
}

Sum of Subarray Minimums

public int sumSubarrayMins(int[] arr) {
    int MOD = 1_000_000_007;
    int n = arr.length;

    // left[i] = count of subarrays ending at i where arr[i] is min
    // right[i] = count of subarrays starting at i where arr[i] is min
    int[] left = new int[n];
    int[] right = new int[n];

    Stack<Integer> stack = new Stack<>();

    // Calculate left boundaries
    for (int i = 0; i < n; i++) {
        while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
            stack.pop();
        }
        left[i] = stack.isEmpty() ? i + 1 : i - stack.peek();
        stack.push(i);
    }

    stack.clear();

    // Calculate right boundaries
    for (int i = n - 1; i >= 0; i--) {
        while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
            stack.pop();
        }
        right[i] = stack.isEmpty() ? n - i : stack.peek() - i;
        stack.push(i);
    }

    // Calculate sum
    long sum = 0;
    for (int i = 0; i < n; i++) {
        sum = (sum + (long) arr[i] * left[i] * right[i]) % MOD;
    }
    return (int) sum;
}

132 Pattern

public boolean find132pattern(int[] nums) {
    int n = nums.length;
    if (n < 3) return false;

    Stack<Integer> stack = new Stack<>();
    int third = Integer.MIN_VALUE; // The "2" in 132

    // Traverse from right to left
    for (int i = n - 1; i >= 0; i--) {
        if (nums[i] < third) return true; // Found "1" < "2"

        while (!stack.isEmpty() && nums[i] > stack.peek()) {
            third = stack.pop(); // Update "2" to be as large as possible
        }
        stack.push(nums[i]);
    }
    return false;
}

13. Greedy

Description

Makes locally optimal choices at each step hoping to find global optimum. Works when problem has optimal substructure.

When to Use

  • Interval scheduling
  • Activity selection
  • Huffman coding
  • Minimum spanning trees
  • Jump game problems
  • Gas station problems

Example Problems

Problem Difficulty Key Insight
Jump Game Medium Track max reachable
Jump Game II Medium BFS-like level jumps
Gas Station Medium If total >= 0, solution exists
Candy Hard Two passes (left, right)
Task Scheduler Medium Fill gaps around max freq
Partition Labels Medium Track last occurrence

Java Code Snippets

Jump Game

public boolean canJump(int[] nums) {
    int maxReach = 0;
    for (int i = 0; i < nums.length; i++) {
        if (i > maxReach) return false;
        maxReach = Math.max(maxReach, i + nums[i]);
    }
    return true;
}

Jump Game II (Minimum Jumps)

public int jump(int[] nums) {
    int jumps = 0, currentEnd = 0, farthest = 0;

    for (int i = 0; i < nums.length - 1; i++) {
        farthest = Math.max(farthest, i + nums[i]);

        if (i == currentEnd) {
            jumps++;
            currentEnd = farthest;
        }
    }
    return jumps;
}

Gas Station

public int canCompleteCircuit(int[] gas, int[] cost) {
    int totalTank = 0, currentTank = 0, startStation = 0;

    for (int i = 0; i < gas.length; i++) {
        int diff = gas[i] - cost[i];
        totalTank += diff;
        currentTank += diff;

        if (currentTank < 0) {
            startStation = i + 1;
            currentTank = 0;
        }
    }
    return totalTank >= 0 ? startStation : -1;
}

Partition Labels

public List<Integer> partitionLabels(String s) {
    // Track last occurrence of each character
    int[] last = new int[26];
    for (int i = 0; i < s.length(); i++) {
        last[s.charAt(i) - 'a'] = i;
    }

    List<Integer> result = new ArrayList<>();
    int start = 0, end = 0;

    for (int i = 0; i < s.length(); i++) {
        end = Math.max(end, last[s.charAt(i) - 'a']);
        if (i == end) {
            result.add(end - start + 1);
            start = end + 1;
        }
    }
    return result;
}

Candy Distribution

public int candy(int[] ratings) {
    int n = ratings.length;
    int[] candies = new int[n];
    Arrays.fill(candies, 1);

    // Left to right: higher rating than left neighbor
    for (int i = 1; i < n; i++) {
        if (ratings[i] > ratings[i - 1]) {
            candies[i] = candies[i - 1] + 1;
        }
    }

    // Right to left: higher rating than right neighbor
    for (int i = n - 2; i >= 0; i--) {
        if (ratings[i] > ratings[i + 1]) {
            candies[i] = Math.max(candies[i], candies[i + 1] + 1);
        }
    }

    int total = 0;
    for (int c : candies) total += c;
    return total;
}

14. Bit Manipulation

Description

Operations directly on binary representations. Very efficient for certain problems involving powers of 2, XOR operations, or compact state representation.

When to Use

  • Single number problems (XOR)
  • Counting bits
  • Power of 2 checks
  • Subset generation
  • Bit masking in DP
  • Finding missing/duplicate numbers

Example Problems

Problem Difficulty Key Insight
Single Number Easy XOR all elements
Single Number II Medium Count bits mod 3
Missing Number Easy XOR with indices
Counting Bits Easy DP with bit shift
Reverse Bits Easy Shift and build
Subsets (using bits) Medium 2^n bitmasks

Common Bit Operations

// Check if i-th bit is set
boolean isSet = (n & (1 << i)) != 0;

// Set i-th bit
n = n | (1 << i);

// Clear i-th bit
n = n & ~(1 << i);

// Toggle i-th bit
n = n ^ (1 << i);

// Check if power of 2
boolean isPowerOf2 = (n & (n - 1)) == 0 && n > 0;

// Get lowest set bit
int lowestBit = n & (-n);

// Clear lowest set bit
n = n & (n - 1);

// Count set bits
int count = Integer.bitCount(n);

Java Code Snippets

Single Number

public int singleNumber(int[] nums) {
    int result = 0;
    for (int num : nums) {
        result ^= num;
    }
    return result;
}

Single Number II (Every element appears 3 times except one)

public int singleNumber2(int[] nums) {
    int ones = 0, twos = 0;
    for (int num : nums) {
        ones = (ones ^ num) & ~twos;
        twos = (twos ^ num) & ~ones;
    }
    return ones;
}

Missing Number

public int missingNumber(int[] nums) {
    int xor = nums.length;
    for (int i = 0; i < nums.length; i++) {
        xor ^= i ^ nums[i];
    }
    return xor;
}

Counting Bits

public int[] countBits(int n) {
    int[] dp = new int[n + 1];
    for (int i = 1; i <= n; i++) {
        dp[i] = dp[i >> 1] + (i & 1);
    }
    return dp;
}

Subsets Using Bitmask

public List<List<Integer>> subsets(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
    int n = nums.length;

    for (int mask = 0; mask < (1 << n); mask++) {
        List<Integer> subset = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            if ((mask & (1 << i)) != 0) {
                subset.add(nums[i]);
            }
        }
        result.add(subset);
    }
    return result;
}

15. Prefix Sum

Description

Precomputes cumulative sums to enable O(1) range sum queries. The prefix sum at index i represents the sum of all elements from index 0 to i.

When to Use

  • Range sum queries
  • Subarray sum equals k
  • Find pivot index
  • Product of array except self
  • 2D matrix sum queries
  • Equilibrium index

Example Problems

Problem Difficulty Key Insight
Range Sum Query - Immutable Easy prefix[j] - prefix[i-1]
Subarray Sum Equals K Medium HashMap for complement
Product Except Self Medium Left and right products
Contiguous Array Medium Map balance to index
Find Pivot Index Easy Left sum = Right sum
Range Sum Query 2D Medium 2D prefix sum

Java Code Snippets

Range Sum Query

class NumArray {
    private int[] prefix;

    public NumArray(int[] nums) {
        prefix = new int[nums.length + 1];
        for (int i = 0; i < nums.length; i++) {
            prefix[i + 1] = prefix[i] + nums[i];
        }
    }

    public int sumRange(int left, int right) {
        return prefix[right + 1] - prefix[left];
    }
}

Subarray Sum Equals K

public int subarraySum(int[] nums, int k) {
    Map<Integer, Integer> prefixCount = new HashMap<>();
    prefixCount.put(0, 1);

    int sum = 0, count = 0;
    for (int num : nums) {
        sum += num;
        count += prefixCount.getOrDefault(sum - k, 0);
        prefixCount.put(sum, prefixCount.getOrDefault(sum, 0) + 1);
    }
    return count;
}

Product Except Self

public int[] productExceptSelf(int[] nums) {
    int n = nums.length;
    int[] result = new int[n];

    // Left products
    result[0] = 1;
    for (int i = 1; i < n; i++) {
        result[i] = result[i - 1] * nums[i - 1];
    }

    // Multiply by right products
    int rightProduct = 1;
    for (int i = n - 1; i >= 0; i--) {
        result[i] *= rightProduct;
        rightProduct *= nums[i];
    }
    return result;
}

Contiguous Array (Equal 0s and 1s)

public int findMaxLength(int[] nums) {
    Map<Integer, Integer> map = new HashMap<>();
    map.put(0, -1);

    int maxLen = 0, count = 0;
    for (int i = 0; i < nums.length; i++) {
        count += nums[i] == 1 ? 1 : -1;

        if (map.containsKey(count)) {
            maxLen = Math.max(maxLen, i - map.get(count));
        } else {
            map.put(count, i);
        }
    }
    return maxLen;
}

2D Range Sum Query

class NumMatrix {
    private int[][] prefix;

    public NumMatrix(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        prefix = new int[m + 1][n + 1];

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                prefix[i][j] = matrix[i-1][j-1] + prefix[i-1][j]
                             + prefix[i][j-1] - prefix[i-1][j-1];
            }
        }
    }

    public int sumRegion(int r1, int c1, int r2, int c2) {
        return prefix[r2+1][c2+1] - prefix[r1][c2+1]
             - prefix[r2+1][c1] + prefix[r1][c1];
    }
}

16. Matrix Manipulation

Description

Techniques for traversing and manipulating 2D arrays efficiently, including rotations, spirals, and search.

When to Use

  • Matrix rotation
  • Spiral traversal
  • Diagonal traversal
  • Search in 2D matrix
  • Game boards (Sudoku, Tic-tac-toe)
  • Image processing

Example Problems

Problem Difficulty Key Insight
Rotate Image Medium Transpose + reverse
Spiral Matrix Medium Layer by layer
Set Matrix Zeroes Medium Use first row/col as markers
Search a 2D Matrix Medium Binary search
Valid Sudoku Medium Track rows, cols, boxes
Game of Life Medium Encode states in bits

Java Code Snippets

Rotate Image (90° clockwise)

public void rotate(int[][] matrix) {
    int n = matrix.length;

    // Transpose
    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            int temp = matrix[i][j];
            matrix[i][j] = matrix[j][i];
            matrix[j][i] = temp;
        }
    }

    // Reverse each row
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n / 2; j++) {
            int temp = matrix[i][j];
            matrix[i][j] = matrix[i][n - 1 - j];
            matrix[i][n - 1 - j] = temp;
        }
    }
}

Spiral Matrix

public List<Integer> spiralOrder(int[][] matrix) {
    List<Integer> result = new ArrayList<>();
    if (matrix.length == 0) return result;

    int top = 0, bottom = matrix.length - 1;
    int left = 0, right = matrix[0].length - 1;

    while (top <= bottom && left <= right) {
        // Right
        for (int j = left; j <= right; j++) {
            result.add(matrix[top][j]);
        }
        top++;

        // Down
        for (int i = top; i <= bottom; i++) {
            result.add(matrix[i][right]);
        }
        right--;

        // Left
        if (top <= bottom) {
            for (int j = right; j >= left; j--) {
                result.add(matrix[bottom][j]);
            }
            bottom--;
        }

        // Up
        if (left <= right) {
            for (int i = bottom; i >= top; i--) {
                result.add(matrix[i][left]);
            }
            left++;
        }
    }
    return result;
}

Set Matrix Zeroes (O(1) space)

public void setZeroes(int[][] matrix) {
    int m = matrix.length, n = matrix[0].length;
    boolean firstRowZero = false, firstColZero = false;

    // Check if first row/col has zero
    for (int j = 0; j < n; j++) {
        if (matrix[0][j] == 0) firstRowZero = true;
    }
    for (int i = 0; i < m; i++) {
        if (matrix[i][0] == 0) firstColZero = true;
    }

    // Mark zeros in first row/col
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            if (matrix[i][j] == 0) {
                matrix[i][0] = 0;
                matrix[0][j] = 0;
            }
        }
    }

    // Set zeros based on markers
    for (int i = 1; i < m; i++) {
        for (int j = 1; j < n; j++) {
            if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                matrix[i][j] = 0;
            }
        }
    }

    // Handle first row/col
    if (firstRowZero) {
        for (int j = 0; j < n; j++) matrix[0][j] = 0;
    }
    if (firstColZero) {
        for (int i = 0; i < m; i++) matrix[i][0] = 0;
    }
}

Search a 2D Matrix

public boolean searchMatrix(int[][] matrix, int target) {
    int m = matrix.length, n = matrix[0].length;
    int left = 0, right = m * n - 1;

    while (left <= right) {
        int mid = left + (right - left) / 2;
        int value = matrix[mid / n][mid % n];

        if (value == target) return true;
        else if (value < target) left = mid + 1;
        else right = mid - 1;
    }
    return false;
}

17. Union Find (Disjoint Set)

Description

Data structure to track elements partitioned into disjoint sets. Supports efficient union and find operations with path compression and union by rank.

When to Use

  • Connected components
  • Detecting cycles in undirected graphs
  • Kruskal's MST algorithm
  • Dynamic connectivity
  • Friend circles/network connections
  • Accounts merge problems

Example Problems

Problem Difficulty Key Insight
Number of Connected Components Medium Count roots
Redundant Connection Medium Cycle detection
Accounts Merge Medium Email as nodes
Longest Consecutive Sequence Medium Union consecutive
Number of Islands II Hard Dynamic island count
Satisfiability of Equality Equations Medium == unions, != checks

Java Code Snippets

Union Find Template

class UnionFind {
    private int[] parent;
    private int[] rank;
    private int count; // Number of connected components

    public UnionFind(int n) {
        parent = new int[n];
        rank = new int[n];
        count = n;
        for (int i = 0; i < n; i++) {
            parent[i] = i;
            rank[i] = 1;
        }
    }

    public int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]); // Path compression
        }
        return parent[x];
    }

    public boolean union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);

        if (rootX == rootY) return false;

        // Union by rank
        if (rank[rootX] < rank[rootY]) {
            parent[rootX] = rootY;
        } else if (rank[rootX] > rank[rootY]) {
            parent[rootY] = rootX;
        } else {
            parent[rootY] = rootX;
            rank[rootX]++;
        }
        count--;
        return true;
    }

    public boolean connected(int x, int y) {
        return find(x) == find(y);
    }

    public int getCount() {
        return count;
    }
}

Number of Connected Components

public int countComponents(int n, int[][] edges) {
    UnionFind uf = new UnionFind(n);
    for (int[] edge : edges) {
        uf.union(edge[0], edge[1]);
    }
    return uf.getCount();
}

Redundant Connection

public int[] findRedundantConnection(int[][] edges) {
    int n = edges.length;
    UnionFind uf = new UnionFind(n + 1);

    for (int[] edge : edges) {
        if (!uf.union(edge[0], edge[1])) {
            return edge; // Already connected = cycle
        }
    }
    return new int[0];
}

Longest Consecutive Sequence

public int longestConsecutive(int[] nums) {
    if (nums.length == 0) return 0;

    Map<Integer, Integer> numToIdx = new HashMap<>();
    int n = nums.length;
    UnionFind uf = new UnionFind(n);

    for (int i = 0; i < n; i++) {
        if (numToIdx.containsKey(nums[i])) continue;

        numToIdx.put(nums[i], i);

        if (numToIdx.containsKey(nums[i] - 1)) {
            uf.union(i, numToIdx.get(nums[i] - 1));
        }
        if (numToIdx.containsKey(nums[i] + 1)) {
            uf.union(i, numToIdx.get(nums[i] + 1));
        }
    }

    Map<Integer, Integer> componentSize = new HashMap<>();
    int maxLen = 1;

    for (int i = 0; i < n; i++) {
        int root = uf.find(i);
        int size = componentSize.getOrDefault(root, 0) + 1;
        componentSize.put(root, size);
        maxLen = Math.max(maxLen, size);
    }
    return maxLen;
}

18. Graphs

Description

Non-linear data structure with vertices and edges. Foundation for many complex algorithms.

When to Use

  • Shortest path problems (Dijkstra, Bellman-Ford)
  • Minimum spanning tree (Prim, Kruskal)
  • Network flow
  • Detecting cycles
  • Path existence
  • Social network analysis

Example Problems

Problem Difficulty Key Insight
Network Delay Time Medium Dijkstra's algorithm
Cheapest Flights Medium Bellman-Ford with k stops
Course Schedule II Medium Topological sort
Is Graph Bipartite? Medium 2-coloring BFS/DFS
Clone Graph Medium DFS with visited map
Alien Dictionary Hard Topological sort

Java Code Snippets

Dijkstra's Algorithm

public int networkDelayTime(int[][] times, int n, int k) {
    // Build adjacency list
    Map<Integer, List<int[]>> graph = new HashMap<>();
    for (int[] t : times) {
        graph.computeIfAbsent(t[0], x -> new ArrayList<>()).add(new int[]{t[1], t[2]});
    }

    // Min heap: [distance, node]
    PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
    pq.offer(new int[]{0, k});

    int[] dist = new int[n + 1];
    Arrays.fill(dist, Integer.MAX_VALUE);
    dist[k] = 0;

    while (!pq.isEmpty()) {
        int[] curr = pq.poll();
        int d = curr[0], u = curr[1];

        if (d > dist[u]) continue;

        if (graph.containsKey(u)) {
            for (int[] neighbor : graph.get(u)) {
                int v = neighbor[0], w = neighbor[1];
                if (dist[u] + w < dist[v]) {
                    dist[v] = dist[u] + w;
                    pq.offer(new int[]{dist[v], v});
                }
            }
        }
    }

    int maxDist = 0;
    for (int i = 1; i <= n; i++) {
        if (dist[i] == Integer.MAX_VALUE) return -1;
        maxDist = Math.max(maxDist, dist[i]);
    }
    return maxDist;
}

Bellman-Ford (with K stops)

public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
    int[] prices = new int[n];
    Arrays.fill(prices, Integer.MAX_VALUE);
    prices[src] = 0;

    for (int i = 0; i <= k; i++) {
        int[] temp = Arrays.copyOf(prices, n);
        for (int[] flight : flights) {
            int from = flight[0], to = flight[1], price = flight[2];
            if (prices[from] != Integer.MAX_VALUE) {
                temp[to] = Math.min(temp[to], prices[from] + price);
            }
        }
        prices = temp;
    }
    return prices[dst] == Integer.MAX_VALUE ? -1 : prices[dst];
}

Is Graph Bipartite?

public boolean isBipartite(int[][] graph) {
    int n = graph.length;
    int[] colors = new int[n]; // 0: unvisited, 1: color A, -1: color B

    for (int i = 0; i < n; i++) {
        if (colors[i] == 0 && !bfs(graph, i, colors)) {
            return false;
        }
    }
    return true;
}

private boolean bfs(int[][] graph, int start, int[] colors) {
    Queue<Integer> queue = new LinkedList<>();
    queue.offer(start);
    colors[start] = 1;

    while (!queue.isEmpty()) {
        int node = queue.poll();
        for (int neighbor : graph[node]) {
            if (colors[neighbor] == colors[node]) return false;
            if (colors[neighbor] == 0) {
                colors[neighbor] = -colors[node];
                queue.offer(neighbor);
            }
        }
    }
    return true;
}

Detect Cycle in Directed Graph

public boolean hasCycle(int numCourses, int[][] prerequisites) {
    List<List<Integer>> graph = new ArrayList<>();
    for (int i = 0; i < numCourses; i++) {
        graph.add(new ArrayList<>());
    }
    for (int[] pre : prerequisites) {
        graph.get(pre[1]).add(pre[0]);
    }

    int[] state = new int[numCourses]; // 0: unvisited, 1: visiting, 2: visited

    for (int i = 0; i < numCourses; i++) {
        if (hasCycleDFS(graph, i, state)) return true;
    }
    return false;
}

private boolean hasCycleDFS(List<List<Integer>> graph, int node, int[] state) {
    if (state[node] == 1) return true;  // Back edge = cycle
    if (state[node] == 2) return false; // Already processed

    state[node] = 1; // Mark as visiting
    for (int neighbor : graph.get(node)) {
        if (hasCycleDFS(graph, neighbor, state)) return true;
    }
    state[node] = 2; // Mark as visited
    return false;
}

19. Dynamic Programming

Description

Solves complex problems by breaking them into overlapping subproblems and storing results to avoid recomputation.

When to Use

  • Optimization problems (min/max)
  • Counting problems
  • Overlapping subproblems
  • Optimal substructure
  • Decision making at each step

DP Patterns

  1. 0/1 Knapsack: Take or skip items
  2. Unbounded Knapsack: Unlimited items
  3. Longest Common Subsequence: Match sequences
  4. Longest Increasing Subsequence: Monotonic sequences
  5. Matrix Chain: Parenthesization
  6. Palindromic: Substring/subsequence
  7. Grid Paths: 2D traversal
  8. State Machine: Buy/sell stocks

Example Problems

Problem Difficulty Key Insight
Climbing Stairs Easy Fibonacci variant
Coin Change Medium Unbounded knapsack
Longest Common Subsequence Medium 2D DP
Word Break Medium Check all prefixes
Edit Distance Hard Insert/delete/replace
Best Time to Buy and Sell Stock Easy-Hard State machine

Java Code Snippets

Climbing Stairs (Fibonacci)

public int climbStairs(int n) {
    if (n <= 2) return n;
    int prev2 = 1, prev1 = 2;
    for (int i = 3; i <= n; i++) {
        int curr = prev1 + prev2;
        prev2 = prev1;
        prev1 = curr;
    }
    return prev1;
}

Coin Change (Unbounded Knapsack)

public int coinChange(int[] coins, int amount) {
    int[] dp = new int[amount + 1];
    Arrays.fill(dp, amount + 1);
    dp[0] = 0;

    for (int i = 1; i <= amount; i++) {
        for (int coin : coins) {
            if (coin <= i) {
                dp[i] = Math.min(dp[i], dp[i - coin] + 1);
            }
        }
    }
    return dp[amount] > amount ? -1 : dp[amount];
}

0/1 Knapsack

public int knapsack(int[] weights, int[] values, int capacity) {
    int n = weights.length;
    int[] dp = new int[capacity + 1];

    for (int i = 0; i < n; i++) {
        for (int w = capacity; w >= weights[i]; w--) {
            dp[w] = Math.max(dp[w], dp[w - weights[i]] + values[i]);
        }
    }
    return dp[capacity];
}

Longest Common Subsequence

public int longestCommonSubsequence(String text1, String text2) {
    int m = text1.length(), n = text2.length();
    int[][] dp = new int[m + 1][n + 1];

    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }
    return dp[m][n];
}

Longest Increasing Subsequence

public int lengthOfLIS(int[] nums) {
    // O(n log n) solution using binary search
    List<Integer> tails = new ArrayList<>();

    for (int num : nums) {
        int pos = Collections.binarySearch(tails, num);
        if (pos < 0) pos = -(pos + 1);

        if (pos == tails.size()) {
            tails.add(num);
        } else {
            tails.set(pos, num);
        }
    }
    return tails.size();
}

Edit Distance

public int minDistance(String word1, String word2) {
    int m = word1.length(), n = word2.length();
    int[][] dp = new int[m + 1][n + 1];

    for (int i = 0; i <= m; i++) dp[i][0] = i;
    for (int j = 0; j <= n; j++) dp[0][j] = j;

    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= n; j++) {
            if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = 1 + Math.min(dp[i - 1][j - 1], // Replace
                               Math.min(dp[i - 1][j],     // Delete
                                       dp[i][j - 1]));    // Insert
            }
        }
    }
    return dp[m][n];
}

Word Break

public boolean wordBreak(String s, List<String> wordDict) {
    Set<String> dict = new HashSet<>(wordDict);
    boolean[] dp = new boolean[s.length() + 1];
    dp[0] = true;

    for (int i = 1; i <= s.length(); i++) {
        for (int j = 0; j < i; j++) {
            if (dp[j] && dict.contains(s.substring(j, i))) {
                dp[i] = true;
                break;
            }
        }
    }
    return dp[s.length()];
}

Best Time to Buy and Sell Stock (State Machine)

// Multiple transactions allowed
public int maxProfit(int[] prices) {
    int hold = Integer.MIN_VALUE, cash = 0;

    for (int price : prices) {
        int prevHold = hold;
        hold = Math.max(hold, cash - price);    // Buy
        cash = Math.max(cash, prevHold + price); // Sell
    }
    return cash;
}

// With cooldown
public int maxProfitWithCooldown(int[] prices) {
    int hold = Integer.MIN_VALUE, cash = 0, cooldown = 0;

    for (int price : prices) {
        int prevCash = cash;
        cash = Math.max(cash, hold + price);     // Sell
        hold = Math.max(hold, cooldown - price); // Buy
        cooldown = prevCash;                     // Cooldown
    }
    return cash;
}

20. Trie (Prefix Tree)

Description

Tree data structure for efficient storage and retrieval of strings, particularly for prefix-based operations.

When to Use

  • Autocomplete/typeahead
  • Spell checker
  • IP routing (longest prefix match)
  • Word search problems
  • Dictionary implementation
  • String matching with wildcards

Example Problems

Problem Difficulty Key Insight
Implement Trie Medium Node array for children
Word Search II Hard Trie + backtracking
Design Add and Search Words Medium DFS for wildcard
Replace Words Medium Find shortest prefix
Longest Word in Dictionary Medium BFS level by level
Palindrome Pairs Hard Trie for reversed words

Java Code Snippets

Trie Implementation

class Trie {
    private TrieNode root;

    class TrieNode {
        TrieNode[] children = new TrieNode[26];
        boolean isEnd = false;
    }

    public Trie() {
        root = new TrieNode();
    }

    public void insert(String word) {
        TrieNode node = root;
        for (char c : word.toCharArray()) {
            int idx = c - 'a';
            if (node.children[idx] == null) {
                node.children[idx] = new TrieNode();
            }
            node = node.children[idx];
        }
        node.isEnd = true;
    }

    public boolean search(String word) {
        TrieNode node = searchPrefix(word);
        return node != null && node.isEnd;
    }

    public boolean startsWith(String prefix) {
        return searchPrefix(prefix) != null;
    }

    private TrieNode searchPrefix(String prefix) {
        TrieNode node = root;
        for (char c : prefix.toCharArray()) {
            int idx = c - 'a';
            if (node.children[idx] == null) return null;
            node = node.children[idx];
        }
        return node;
    }
}

Word Search II

public List<String> findWords(char[][] board, String[] words) {
    List<String> result = new ArrayList<>();
    TrieNode root = buildTrie(words);

    for (int i = 0; i < board.length; i++) {
        for (int j = 0; j < board[0].length; j++) {
            dfs(board, i, j, root, result);
        }
    }
    return result;
}

private void dfs(char[][] board, int i, int j, TrieNode node, List<String> result) {
    char c = board[i][j];
    if (c == '#' || node.children[c - 'a'] == null) return;

    node = node.children[c - 'a'];
    if (node.word != null) {
        result.add(node.word);
        node.word = null; // Avoid duplicates
    }

    board[i][j] = '#';
    int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    for (int[] d : dirs) {
        int ni = i + d[0], nj = j + d[1];
        if (ni >= 0 && ni < board.length && nj >= 0 && nj < board[0].length) {
            dfs(board, ni, nj, node, result);
        }
    }
    board[i][j] = c;
}

private TrieNode buildTrie(String[] words) {
    TrieNode root = new TrieNode();
    for (String word : words) {
        TrieNode node = root;
        for (char c : word.toCharArray()) {
            int idx = c - 'a';
            if (node.children[idx] == null) {
                node.children[idx] = new TrieNode();
            }
            node = node.children[idx];
        }
        node.word = word;
    }
    return root;
}

class TrieNode {
    TrieNode[] children = new TrieNode[26];
    String word;
}

21. Topological Sort

Description

Linear ordering of vertices in a directed acyclic graph (DAG) where for every edge (u, v), u comes before v.

When to Use

  • Task scheduling with dependencies
  • Course prerequisites
  • Build systems
  • Package dependencies
  • Detecting cycles in directed graphs

Example Problems

Problem Difficulty Key Insight
Course Schedule Medium Cycle detection
Course Schedule II Medium Return order
Alien Dictionary Hard Build graph from words
Parallel Courses Medium BFS levels = time
Sequence Reconstruction Medium Unique topological order
Minimum Height Trees Medium Leaf removal

Java Code Snippets

Kahn's Algorithm (BFS)

public int[] findOrder(int numCourses, int[][] prerequisites) {
    List<List<Integer>> graph = new ArrayList<>();
    int[] indegree = new int[numCourses];

    for (int i = 0; i < numCourses; i++) {
        graph.add(new ArrayList<>());
    }

    for (int[] pre : prerequisites) {
        graph.get(pre[1]).add(pre[0]);
        indegree[pre[0]]++;
    }

    Queue<Integer> queue = new LinkedList<>();
    for (int i = 0; i < numCourses; i++) {
        if (indegree[i] == 0) queue.offer(i);
    }

    int[] order = new int[numCourses];
    int idx = 0;

    while (!queue.isEmpty()) {
        int course = queue.poll();
        order[idx++] = course;

        for (int next : graph.get(course)) {
            if (--indegree[next] == 0) {
                queue.offer(next);
            }
        }
    }

    return idx == numCourses ? order : new int[0];
}

DFS-based Topological Sort

public int[] topologicalSortDFS(int numCourses, int[][] prerequisites) {
    List<List<Integer>> graph = new ArrayList<>();
    for (int i = 0; i < numCourses; i++) {
        graph.add(new ArrayList<>());
    }
    for (int[] pre : prerequisites) {
        graph.get(pre[1]).add(pre[0]);
    }

    int[] state = new int[numCourses]; // 0: unvisited, 1: visiting, 2: visited
    Stack<Integer> stack = new Stack<>();

    for (int i = 0; i < numCourses; i++) {
        if (state[i] == 0) {
            if (!dfs(graph, i, state, stack)) {
                return new int[0]; // Cycle detected
            }
        }
    }

    int[] result = new int[numCourses];
    for (int i = 0; i < numCourses; i++) {
        result[i] = stack.pop();
    }
    return result;
}

private boolean dfs(List<List<Integer>> graph, int node, int[] state, Stack<Integer> stack) {
    state[node] = 1; // Visiting

    for (int neighbor : graph.get(node)) {
        if (state[neighbor] == 1) return false; // Cycle
        if (state[neighbor] == 0 && !dfs(graph, neighbor, state, stack)) {
            return false;
        }
    }

    state[node] = 2; // Visited
    stack.push(node);
    return true;
}

Alien Dictionary

public String alienOrder(String[] words) {
    Map<Character, Set<Character>> graph = new HashMap<>();
    Map<Character, Integer> indegree = new HashMap<>();

    // Initialize
    for (String word : words) {
        for (char c : word.toCharArray()) {
            graph.putIfAbsent(c, new HashSet<>());
            indegree.putIfAbsent(c, 0);
        }
    }

    // Build graph
    for (int i = 0; i < words.length - 1; i++) {
        String w1 = words[i], w2 = words[i + 1];
        if (w1.length() > w2.length() && w1.startsWith(w2)) {
            return ""; // Invalid
        }

        for (int j = 0; j < Math.min(w1.length(), w2.length()); j++) {
            if (w1.charAt(j) != w2.charAt(j)) {
                if (!graph.get(w1.charAt(j)).contains(w2.charAt(j))) {
                    graph.get(w1.charAt(j)).add(w2.charAt(j));
                    indegree.put(w2.charAt(j), indegree.get(w2.charAt(j)) + 1);
                }
                break;
            }
        }
    }

    // Topological sort
    Queue<Character> queue = new LinkedList<>();
    for (char c : indegree.keySet()) {
        if (indegree.get(c) == 0) queue.offer(c);
    }

    StringBuilder result = new StringBuilder();
    while (!queue.isEmpty()) {
        char c = queue.poll();
        result.append(c);
        for (char next : graph.get(c)) {
            indegree.put(next, indegree.get(next) - 1);
            if (indegree.get(next) == 0) queue.offer(next);
        }
    }

    return result.length() == indegree.size() ? result.toString() : "";
}

22. Segment Tree

Description

Tree data structure for efficient range queries and updates on arrays. Supports O(log n) updates and queries.

When to Use

  • Range sum/min/max queries with updates
  • Interval problems with modifications
  • Counting inversions
  • Lazy propagation for range updates

Example Problems

Problem Difficulty Key Insight
Range Sum Query - Mutable Medium Basic segment tree
Count of Smaller Numbers After Self Hard Merge sort or segment tree
Range Sum Query 2D - Mutable Hard 2D segment tree
Falling Squares Hard Coordinate compression
My Calendar III Hard Track overlapping events
Count of Range Sum Hard Merge sort variant

Java Code Snippets

Segment Tree Implementation

class SegmentTree {
    private int[] tree;
    private int[] nums;
    private int n;

    public SegmentTree(int[] nums) {
        this.nums = nums;
        this.n = nums.length;
        this.tree = new int[4 * n];
        build(0, 0, n - 1);
    }

    private void build(int node, int start, int end) {
        if (start == end) {
            tree[node] = nums[start];
        } else {
            int mid = start + (end - start) / 2;
            build(2 * node + 1, start, mid);
            build(2 * node + 2, mid + 1, end);
            tree[node] = tree[2 * node + 1] + tree[2 * node + 2];
        }
    }

    public void update(int idx, int val) {
        update(0, 0, n - 1, idx, val);
    }

    private void update(int node, int start, int end, int idx, int val) {
        if (start == end) {
            nums[idx] = val;
            tree[node] = val;
        } else {
            int mid = start + (end - start) / 2;
            if (idx <= mid) {
                update(2 * node + 1, start, mid, idx, val);
            } else {
                update(2 * node + 2, mid + 1, end, idx, val);
            }
            tree[node] = tree[2 * node + 1] + tree[2 * node + 2];
        }
    }

    public int query(int left, int right) {
        return query(0, 0, n - 1, left, right);
    }

    private int query(int node, int start, int end, int left, int right) {
        if (right < start || end < left) {
            return 0; // Out of range
        }
        if (left <= start && end <= right) {
            return tree[node]; // Fully within range
        }
        int mid = start + (end - start) / 2;
        return query(2 * node + 1, start, mid, left, right)
             + query(2 * node + 2, mid + 1, end, left, right);
    }
}

Count of Smaller Numbers After Self

public List<Integer> countSmaller(int[] nums) {
    int n = nums.length;
    Integer[] result = new Integer[n];
    int[][] indexed = new int[n][2]; // [value, original index]

    for (int i = 0; i < n; i++) {
        indexed[i] = new int[]{nums[i], i};
        result[i] = 0;
    }

    mergeSort(indexed, 0, n - 1, result);
    return Arrays.asList(result);
}

private void mergeSort(int[][] arr, int left, int right, Integer[] result) {
    if (left >= right) return;

    int mid = left + (right - left) / 2;
    mergeSort(arr, left, mid, result);
    mergeSort(arr, mid + 1, right, result);
    merge(arr, left, mid, right, result);
}

private void merge(int[][] arr, int left, int mid, int right, Integer[] result) {
    int[][] temp = new int[right - left + 1][2];
    int i = left, j = mid + 1, k = 0;

    while (i <= mid && j <= right) {
        if (arr[i][0] <= arr[j][0]) {
            // Count elements from right half that are smaller
            result[arr[i][1]] += j - mid - 1;
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }

    while (i <= mid) {
        result[arr[i][1]] += j - mid - 1;
        temp[k++] = arr[i++];
    }

    while (j <= right) {
        temp[k++] = arr[j++];
    }

    System.arraycopy(temp, 0, arr, left, temp.length);
}

23. Binary Indexed Tree (Fenwick Tree)

Description

Data structure providing O(log n) prefix sum queries and point updates. More memory efficient than segment trees for certain operations.

When to Use

  • Prefix sum queries with updates
  • Counting inversions
  • Range sum queries
  • Cumulative frequency tables

Example Problems

Problem Difficulty Key Insight
Range Sum Query - Mutable Medium Basic BIT
Count of Smaller Numbers After Self Hard Discretization + BIT
Reverse Pairs Hard Merge sort or BIT
Create Sorted Array through Instructions Hard BIT for counting
Global and Local Inversions Medium BIT or observation

Java Code Snippets

Binary Indexed Tree Implementation

class BinaryIndexedTree {
    private int[] tree;
    private int n;

    public BinaryIndexedTree(int n) {
        this.n = n;
        this.tree = new int[n + 1];
    }

    // Add val to index i (1-indexed)
    public void update(int i, int val) {
        while (i <= n) {
            tree[i] += val;
            i += i & (-i); // Add lowest set bit
        }
    }

    // Get prefix sum [1, i]
    public int query(int i) {
        int sum = 0;
        while (i > 0) {
            sum += tree[i];
            i -= i & (-i); // Remove lowest set bit
        }
        return sum;
    }

    // Get range sum [left, right]
    public int rangeQuery(int left, int right) {
        return query(right) - query(left - 1);
    }
}

Range Sum Query - Mutable (using BIT)

class NumArray {
    private BinaryIndexedTree bit;
    private int[] nums;

    public NumArray(int[] nums) {
        this.nums = nums;
        this.bit = new BinaryIndexedTree(nums.length);
        for (int i = 0; i < nums.length; i++) {
            bit.update(i + 1, nums[i]);
        }
    }

    public void update(int index, int val) {
        int diff = val - nums[index];
        nums[index] = val;
        bit.update(index + 1, diff);
    }

    public int sumRange(int left, int right) {
        return bit.rangeQuery(left + 1, right + 1);
    }
}

24. Two Heaps

Description

Uses two heaps (max-heap and min-heap) to efficiently track median or partition elements.

When to Use

  • Finding median from data stream
  • Sliding window median
  • Balancing two groups
  • IPO problem (maximizing profit)

Example Problems

Problem Difficulty Key Insight
Find Median from Data Stream Hard Max heap for lower, min for upper
Sliding Window Median Hard Two heaps + lazy removal
IPO Hard Max heap for profit, min for capital
Balance a Binary Search Tree Medium Sort + rebuild

Java Code Snippets

Find Median from Data Stream

class MedianFinder {
    private PriorityQueue<Integer> maxHeap; // Lower half
    private PriorityQueue<Integer> minHeap; // Upper half

    public MedianFinder() {
        maxHeap = new PriorityQueue<>(Collections.reverseOrder());
        minHeap = new PriorityQueue<>();
    }

    public void addNum(int num) {
        maxHeap.offer(num);
        minHeap.offer(maxHeap.poll());

        // Balance: maxHeap can have at most 1 more element
        if (maxHeap.size() < minHeap.size()) {
            maxHeap.offer(minHeap.poll());
        }
    }

    public double findMedian() {
        if (maxHeap.size() > minHeap.size()) {
            return maxHeap.peek();
        }
        return (maxHeap.peek() + minHeap.peek()) / 2.0;
    }
}

Sliding Window Median

public double[] medianSlidingWindow(int[] nums, int k) {
    double[] result = new double[nums.length - k + 1];
    TreeMap<Integer, Integer> lower = new TreeMap<>(Collections.reverseOrder());
    TreeMap<Integer, Integer> upper = new TreeMap<>();
    int lowerSize = 0, upperSize = 0;

    for (int i = 0; i < nums.length; i++) {
        // Add number
        if (lower.isEmpty() || nums[i] <= lower.firstKey()) {
            add(lower, nums[i]);
            lowerSize++;
        } else {
            add(upper, nums[i]);
            upperSize++;
        }

        // Remove old number
        if (i >= k) {
            int old = nums[i - k];
            if (lower.containsKey(old)) {
                remove(lower, old);
                lowerSize--;
            } else {
                remove(upper, old);
                upperSize--;
            }
        }

        // Balance
        while (lowerSize > upperSize + 1) {
            int move = lower.firstKey();
            remove(lower, move);
            lowerSize--;
            add(upper, move);
            upperSize++;
        }
        while (upperSize > lowerSize) {
            int move = upper.firstKey();
            remove(upper, move);
            upperSize--;
            add(lower, move);
            lowerSize++;
        }

        // Calculate median
        if (i >= k - 1) {
            if (k % 2 == 1) {
                result[i - k + 1] = lower.firstKey();
            } else {
                result[i - k + 1] = ((double) lower.firstKey() + upper.firstKey()) / 2;
            }
        }
    }
    return result;
}

private void add(TreeMap<Integer, Integer> map, int num) {
    map.put(num, map.getOrDefault(num, 0) + 1);
}

private void remove(TreeMap<Integer, Integer> map, int num) {
    map.put(num, map.get(num) - 1);
    if (map.get(num) == 0) map.remove(num);
}

25. Cyclic Sort

Description

Sorts arrays where elements are in a known range [1, n] by placing each element at its correct index in O(n) time.

When to Use

  • Find missing number
  • Find duplicate
  • Find all missing numbers
  • First missing positive
  • Array contains numbers in range [1, n]

Example Problems

Problem Difficulty Key Insight
Missing Number Easy XOR or cyclic sort
Find All Numbers Disappeared Easy Mark as negative
Find the Duplicate Number Medium Cyclic sort or Floyd's
First Missing Positive Hard Place each in correct position
Find All Duplicates Medium Mark and detect

Java Code Snippets

Cyclic Sort Template

public void cyclicSort(int[] nums) {
    int i = 0;
    while (i < nums.length) {
        int correctIdx = nums[i] - 1;
        if (nums[i] > 0 && nums[i] <= nums.length
            && nums[i] != nums[correctIdx]) {
            // Swap to correct position
            int temp = nums[i];
            nums[i] = nums[correctIdx];
            nums[correctIdx] = temp;
        } else {
            i++;
        }
    }
}

First Missing Positive

public int firstMissingPositive(int[] nums) {
    int n = nums.length;

    // Place each number in its correct position
    for (int i = 0; i < n; i++) {
        while (nums[i] > 0 && nums[i] <= n
               && nums[nums[i] - 1] != nums[i]) {
            int correctIdx = nums[i] - 1;
            int temp = nums[i];
            nums[i] = nums[correctIdx];
            nums[correctIdx] = temp;
        }
    }

    // Find first missing
    for (int i = 0; i < n; i++) {
        if (nums[i] != i + 1) {
            return i + 1;
        }
    }
    return n + 1;
}

Find All Duplicates

public List<Integer> findDuplicates(int[] nums) {
    List<Integer> result = new ArrayList<>();

    for (int i = 0; i < nums.length; i++) {
        int idx = Math.abs(nums[i]) - 1;
        if (nums[idx] < 0) {
            result.add(idx + 1);
        }
        nums[idx] = -nums[idx];
    }
    return result;
}

Quick Pattern Recognition Cheat Sheet

If you see... Consider...
Sorted array Binary Search, Two Pointers
Find pairs/triplets Two Pointers, HashMap
Linked list cycle Fast & Slow Pointers
Contiguous subarray Sliding Window, Prefix Sum
Start/end times Intervals, Sorting
Matching brackets Stack
Shortest path (unweighted) BFS
Shortest path (weighted) Dijkstra, Bellman-Ford
All paths/permutations Backtracking, DFS
Connected components Union Find, DFS
Tree traversal DFS, BFS
Dependencies Topological Sort
K largest/smallest Heap
String prefix Trie
Range queries + updates Segment Tree, BIT
Optimization (min/max) DP, Greedy
Counting ways DP
Median stream Two Heaps
Numbers in range [1,n] Cyclic Sort
XOR properties Bit Manipulation
Previous/next greater Monotonic Stack

Time Complexity Quick Reference

Pattern Time Space
Two Pointers O(n) O(1)
Sliding Window O(n) O(k)
Binary Search O(log n) O(1)
BFS/DFS O(V + E) O(V)
Heap operations O(log n) O(n)
Union Find O(α(n)) ≈ O(1) O(n)
Trie insert/search O(m) O(ALPHABET × m × n)
Segment Tree O(log n) O(n)
DP (2D) O(m × n) O(m × n) or O(n)
Backtracking O(k^n) or O(n!) O(n)
Topological Sort O(V + E) O(V)

Interview Tips

  1. Clarify first: Ask about constraints, edge cases, expected input size
  2. State pattern: Mention the pattern you're using and why
  3. Start simple: Begin with brute force if needed, then optimize
  4. Test thoroughly: Walk through examples before coding
  5. Consider edge cases: Empty input, single element, duplicates
  6. Analyze complexity: State time and space complexity
  7. Optimize: Consider if there's a better approach after solving