Library / Java & Design

Java Interview Templates — Source

935 lines of Java
import java.util.*;

/** Java 8-compatible interview templates. Read each method's contract before reuse. */
public class JavaInterviewTemplates {
    // BEGIN 01 | Arrays and lists | Essential
    static void arraysAndLists() {
        int[] a = {3, 1, 2};
        int[] copy = a.clone();
        int[] slice = Arrays.copyOfRange(a, 0, 2); // [0, 2)
        Arrays.sort(copy);                          // mutates copy
        int[] filled = new int[5];
        Arrays.fill(filled, -1);
        int[][] matrix = new int[2][3];
        for (int[] row : matrix) Arrays.fill(row, -1);
        int[][] deepCopy = new int[matrix.length][];
        for (int i = 0; i < matrix.length; i++) deepCopy[i] = matrix[i].clone();
        boolean equal = Arrays.equals(a, copy);
        boolean deepEqual = Arrays.deepEquals(matrix, deepCopy);
        List<Integer> list = new ArrayList<>(Arrays.asList(3, 1, 2));
        list.add(4);
        list.remove(Integer.valueOf(1)); // remove value 1
        list.remove(0);                 // remove index 0
        Collections.sort(list);
        Collections.reverse(list);
        Integer[] boxed = list.toArray(new Integer[0]);
        int[] primitive = list.stream().mapToInt(Integer::intValue).toArray();
        // Arrays.asList(new int[]{1, 2}) is a List<int[]> containing ONE array.
        // Arrays.asList(1, 2) is fixed-size; wrap in ArrayList to add/remove.
        // a.length; list.size(); Arrays.toString(a); Arrays.deepToString(matrix).
    }
    // END

    // BEGIN 02 | Strings and characters | Essential
    static String stringTools(String s) {
        int n = s.length();
        if (n > 0) {
            char first = s.charAt(0);
            String rest = s.substring(1); // valid even when n == 1
            String prefix = s.substring(0, Math.min(3, n)); // end exclusive
        }
        char[] chars = s.toCharArray();
        Arrays.sort(chars);
        String sorted = new String(chars);
        String[] words = s.trim().isEmpty() ? new String[0] : s.trim().split("\\s+");
        String[] dotted = "a.b.".split("\\.", -1); // regex; preserve trailing empty
        boolean equal = Objects.equals(s, sorted); // null-safe equality
        int comparison = s.compareTo(sorted);     // lexicographic order
        StringBuilder out = new StringBuilder();
        for (char c : s.toCharArray()) out.append(c);
        if (out.length() > 0) out.deleteCharAt(out.length() - 1);
        return out.reverse().toString();
        // String is immutable. Repeated concatenation in a loop can be quadratic.
        // char is a UTF-16 code unit; use s.codePoints().toArray() if required.
        // Lowercase-English counting only: int[] count = new int[26]; count[c-'a']++.
    }
    // END

    // BEGIN 03 | HashMap and HashSet | Essential
    static Map<Integer, Integer> frequencies(int[] a) {
        Map<Integer, Integer> freq = new HashMap<>();
        for (int x : a) freq.put(x, freq.getOrDefault(x, 0) + 1);
        // Equivalent increment: freq.merge(x, 1, Integer::sum);
        Set<Integer> seen = new HashSet<>();
        for (int x : a) {
            if (!seen.add(x)) { /* x was already present */ }
        }
        Map<String, List<Integer>> groups = new HashMap<>();
        groups.computeIfAbsent("key", k -> new ArrayList<>()).add(7);
        for (Map.Entry<Integer, Integer> entry : freq.entrySet()) {
            int value = entry.getKey(), count = entry.getValue();
        }
        // Decrement and remove zero counts (when x is present):
        // int next = freq.get(x) - 1;
        // if (next == 0) freq.remove(x); else freq.put(x, next);
        // Use iterator.remove() to remove during iteration.
        return freq;
    }
    // END

    // BEGIN 04 | Queue stack and deque | Essential
    static void dequeTools() {
        Queue<Integer> queue = new ArrayDeque<>();
        queue.offer(10);
        Integer head = queue.peek(); // null if empty
        Integer removed = queue.poll();
        Deque<Integer> stack = new ArrayDeque<>();
        stack.push(10);
        int top = stack.peek();     // unboxing null would throw if empty
        int popped = stack.pop();   // throws if empty
        Deque<Integer> deque = new ArrayDeque<>();
        deque.offerFirst(1);
        deque.offerLast(2);
        deque.peekFirst();
        deque.peekLast();
        deque.pollFirst();
        deque.pollLast();
        // ArrayDeque rejects null. Keep queue and stack conventions distinct.
    }
    // END

    // BEGIN 05 | Heaps comparators and top-k | Essential
    static int kthLargestHeap(int[] a, int k) {
        if (k < 1 || k > a.length) throw new IllegalArgumentException("k");
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        for (int x : a) {
            minHeap.offer(x);
            if (minHeap.size() > k) minHeap.poll();
        }
        return minHeap.peek(); // O(n log(k+1)) time, O(k) space
    }
    static void comparatorTools() {
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
        PriorityQueue<int[]> byCost = new PriorityQueue<>((a, b) -> {
            int cmp = Integer.compare(a[1], b[1]);
            return cmp != 0 ? cmp : Integer.compare(a[0], b[0]);
        });
        int[][] intervals = {{2, 4}, {1, 5}, {1, 3}};
        Arrays.sort(intervals, (a, b) -> {
            int cmp = Integer.compare(a[0], b[0]);
            return cmp != 0 ? cmp : Integer.compare(a[1], b[1]);
        });
        // Never use a[0] - b[0] as a comparator: overflow can break ordering.
        // Primitive int[] cannot take a comparator; int[][] can (rows are objects).
        // Heap iteration is NOT sorted. Repeated poll() retrieves sorted order.
        // Do not mutate a queued element's priority; reinsert a new entry instead.
    }
    // END

    // BEGIN 06 | Ordered maps sets and multisets | Essential
    static void orderedTools() {
        TreeMap<Integer, Integer> counts = new TreeMap<>();
        counts.merge(10, 1, Integer::sum);
        counts.merge(10, 1, Integer::sum); // frequency 2
        counts.merge(20, 1, Integer::sum);
        Integer floor = counts.floorKey(15);   // <= 15
        Integer ceiling = counts.ceilingKey(15); // >= 15
        Integer lower = counts.lowerKey(10);   // < 10; null here
        Integer higher = counts.higherKey(10); // > 10
        int smallest = counts.firstKey(); // requires nonempty map
        int largest = counts.lastKey();
        int x = 10;
        if (counts.get(x) == 1) counts.remove(x);
        else counts.put(x, counts.get(x) - 1);
        NavigableSet<Integer> set = new TreeSet<>();
        set.add(10); set.add(20);
        Integer candidate = set.ceiling(11);
        // TreeSet removes duplicates according to its comparator (compare == 0).
    }
    // END

    // BEGIN 07 | Numeric safety and bit operations | Essential
    static long gcd(long a, long b) { // nonnegative inputs
        while (b != 0) { long remainder = a % b; a = b; b = remainder; }
        return a;
    }
    static long modPow(long base, long exponent, int mod) {
        // exponent >= 0, mod > 0; int modulus keeps products within long range.
        long result = 1 % mod;
        base = Math.floorMod(base, (long) mod);
        while (exponent > 0) {
            if ((exponent & 1L) != 0) result = result * base % mod;
            base = base * base % mod;
            exponent >>= 1;
        }
        return result;
    }
    static void numericTools() {
        int a = Integer.MAX_VALUE, b = 2;
        long product = (long) a * b; // cast BEFORE arithmetic
        long sum = (long) a + b;
        long absolute = Math.abs((long) Integer.MIN_VALUE);
        long n = 15, d = 4; // n >= 0, d > 0
        long ceilDiv = n / d + (n % d == 0 ? 0 : 1); // no n+d overflow
        int normalized = Math.floorMod(-3, 5);
        int mask = 12, bit = 2; // 0 <= bit < 32 for int
        boolean isSet = (mask & (1 << bit)) != 0;
        int set = mask | (1 << bit), clear = mask & ~(1 << bit);
        int toggle = mask ^ (1 << bit), lowest = mask & -mask;
        int withoutLowest = mask & (mask - 1);
        int count = Integer.bitCount(mask), logicalShift = mask >>> 1;
        boolean powerOfTwo = mask > 0 && (mask & (mask - 1)) == 0;
        long wideBit = 1L << 40;
        // Java masks shift distances: 1 << 32 equals 1 << 0.
    }
    // END

    // BEGIN 08 | Node definitions | Essential
    static class ListNode {
        int val;
        ListNode next;
        ListNode(int val) { this.val = val; }
    }
    static class TreeNode {
        int val;
        TreeNode left, right;
        TreeNode(int val) { this.val = val; }
    }
    // END

    // BEGIN 09 | Linked-list reverse cycle and merge | Essential
    static ListNode reverseList(ListNode head) { // mutates links; O(n), O(1)
        ListNode previous = null;
        while (head != null) {
            ListNode next = head.next;
            head.next = previous;
            previous = head;
            head = next;
        }
        return previous;
    }
    static boolean hasCycle(ListNode head) { // O(n), O(1)
        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;
    }
    static ListNode mergeLists(ListNode a, ListNode b) {
        // Sorted ascending, acyclic, disjoint lists; reuses and mutates nodes.
        ListNode dummy = new ListNode(0), tail = dummy;
        while (a != null && b != null) {
            if (a.val <= b.val) { tail.next = a; a = a.next; }
            else { tail.next = b; b = b.next; }
            tail = tail.next;
        }
        tail.next = a != null ? a : b;
        return dummy.next; // O(n+m), O(1) auxiliary space
    }
    // END

    // BEGIN 10 | Binary-search boundaries | Essential
    static int lowerBound(int[] a, int target) {
        int left = 0, right = a.length; // search [left, right)
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (a[mid] < target) left = mid + 1;
            else right = mid;
        }
        return left; // first >= target; n if none
    }
    static int upperBound(int[] a, int target) {
        int left = 0, right = a.length;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (a[mid] <= target) left = mid + 1;
            else right = mid;
        }
        return left; // first > target; n if none
    }
    // Sorted ascending input. O(log n), O(1).
    // count(target) = upperBound(a,target) - lowerBound(a,target).
    // Exact match: int i = lowerBound(a,t); boolean found = i < a.length && a[i] == t;
    // END

    // BEGIN 11 | Binary search on the answer | Essential
    static int minEatingSpeed(int[] piles, long hours) {
        // Positive piles; one pile per hour, unused capacity cannot transfer.
        if (piles.length == 0) return 0;
        if (hours < piles.length) return -1;
        int left = 1, right = 0;
        for (int pile : piles) right = Math.max(right, pile);
        while (left < right) {
            int speed = left + (right - left) / 2;
            long needed = 0;
            for (int pile : piles) needed += ((long) pile + speed - 1) / speed;
            if (needed <= hours) right = speed; // feasible: seek smaller
            else left = speed + 1;
        }
        return left; // O(n log maxPile), O(1)
    }
    // END

    // BEGIN 12 | Prefix sums and subarray-sum counting | Essential
    static long[] prefixSums(int[] a) {
        long[] prefix = new long[a.length + 1];
        for (int i = 0; i < a.length; i++) prefix[i + 1] = prefix[i] + a[i];
        return prefix; // sum of [left,right) = prefix[right] - prefix[left]
    }
    static long subarraySumCount(int[] a, long target) {
        // Supports negative values; count can exceed int range.
        Map<Long, Long> frequencies = new HashMap<>();
        frequencies.put(0L, 1L);
        long prefix = 0, answer = 0;
        for (int x : a) {
            prefix += x;
            answer += frequencies.getOrDefault(prefix - target, 0L);
            frequencies.put(prefix, frequencies.getOrDefault(prefix, 0L) + 1);
        }
        return answer; // expected O(n) time, O(n) space
    }
    // END

    // BEGIN 13 | Two pointers on sorted input | Essential
    static int[] twoSumSorted(int[] a, long target) {
        int left = 0, right = a.length - 1;
        while (left < right) {
            long sum = (long) a[left] + a[right];
            if (sum == target) return new int[]{left, right};
            if (sum < target) left++;
            else right--;
        }
        return new int[]{-1, -1}; // O(n), O(1); returns zero-based indices
    }
    // END

    // BEGIN 14 | Variable sliding window | Essential
    static int longestAtMostKDistinct(String s, int k) {
        if (k <= 0) return 0;
        Map<Character, Integer> freq = new HashMap<>();
        int left = 0, best = 0;
        for (int right = 0; right < s.length(); right++) {
            char c = s.charAt(right);
            freq.put(c, freq.getOrDefault(c, 0) + 1);
            while (freq.size() > k) {
                char old = s.charAt(left++);
                int remaining = freq.get(old) - 1;
                if (remaining == 0) freq.remove(old);
                else freq.put(old, remaining);
            }
            best = Math.max(best, right - left + 1);
        }
        return best; // expected O(n); O(min(alphabet,k+1)) space; UTF-16 units
    }
    // END

    // BEGIN 15 | Fixed sliding window | Essential
    static long maxWindowSum(int[] a, int k) {
        if (k < 1 || k > a.length) throw new IllegalArgumentException("k");
        long window = 0, best = Long.MIN_VALUE;
        for (int right = 0; right < a.length; right++) {
            window += a[right];
            if (right >= k) window -= a[right - k];
            if (right >= k - 1) best = Math.max(best, window);
        }
        return best; // O(n), O(1); works with all-negative arrays
    }
    // END

    // BEGIN 16 | Monotonic stack | Essential
    static int[] nextGreaterIndex(int[] a) {
        int[] answer = new int[a.length];
        Arrays.fill(answer, -1);
        Deque<Integer> stack = new ArrayDeque<>();
        for (int i = 0; i < a.length; i++) {
            while (!stack.isEmpty() && a[stack.peek()] < a[i]) {
                answer[stack.pop()] = i;
            }
            stack.push(i);
        }
        return answer; // O(n), O(n); strictly greater, not greater-or-equal
    }
    // END

    // BEGIN 17 | Monotonic deque for sliding maximum | Important
    static int[] slidingMaximum(int[] a, int k) {
        if (k < 1 || k > a.length) throw new IllegalArgumentException("k");
        int[] answer = new int[a.length - k + 1];
        Deque<Integer> deque = new ArrayDeque<>(); // indices, descending values
        for (int i = 0; i < a.length; i++) {
            while (!deque.isEmpty() && deque.peekFirst() <= i - k) deque.pollFirst();
            while (!deque.isEmpty() && a[deque.peekLast()] <= a[i]) deque.pollLast();
            deque.offerLast(i);
            if (i >= k - 1) answer[i - k + 1] = a[deque.peekFirst()];
        }
        return answer; // O(n), O(k) auxiliary space, excluding output
    }
    // END

    // BEGIN 18 | Merge intervals | Essential
    static int[][] mergeIntervals(int[][] intervals) {
        // Closed intervals [start,end], start <= end; touching intervals merge.
        int[][] sorted = new int[intervals.length][];
        for (int i = 0; i < intervals.length; i++) sorted[i] = intervals[i].clone();
        Arrays.sort(sorted, (a, b) -> Integer.compare(a[0], b[0]));
        List<int[]> result = new ArrayList<>();
        for (int[] current : sorted) {
            if (result.isEmpty() || result.get(result.size() - 1)[1] < current[0]) {
                result.add(current);
            } else {
                int[] last = result.get(result.size() - 1);
                last[1] = Math.max(last[1], current[1]);
            }
        }
        return result.toArray(new int[0][]); // O(n log n), O(n); preserves input
    }
    // END

    // BEGIN 19 | Meeting rooms and endpoint semantics | Important
    static int meetingRooms(int[][] intervals) {
        // Half-open intervals [start,end), start < end; touching meetings coexist.
        int[][] sorted = intervals.clone(); // sorting outer array only; rows unchanged
        Arrays.sort(sorted, (a, b) -> Integer.compare(a[0], b[0]));
        PriorityQueue<Integer> ends = new PriorityQueue<>();
        int best = 0;
        for (int[] interval : sorted) {
            while (!ends.isEmpty() && ends.peek() <= interval[0]) ends.poll();
            ends.offer(interval[1]);
            best = Math.max(best, ends.size());
        }
        return best; // O(n log n), O(n)
    }
    // END

    // BEGIN 20 | Tree inorder and level-order traversal | Essential
    static List<Integer> inorder(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode current = root;
        while (current != null || !stack.isEmpty()) {
            while (current != null) { stack.push(current); current = current.left; }
            current = stack.pop();
            result.add(current.val);
            current = current.right;
        }
        return result; // O(n), O(h) auxiliary space
    }
    static List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) return result;
        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            int size = queue.size(); // snapshot before adding children
            List<Integer> level = new ArrayList<>();
            for (int i = 0; i < size; 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; // O(n), O(maximum width) auxiliary space
    }
    // END

    // BEGIN 21 | Tree recursion balance and LCA | Essential
    static int heightOrUnbalanced(TreeNode root) {
        if (root == null) return 0;
        int left = heightOrUnbalanced(root.left);
        if (left == -1) return -1;
        int right = heightOrUnbalanced(root.right);
        if (right == -1 || Math.abs(left - right) > 1) return -1;
        return 1 + Math.max(left, right); // -1 means unbalanced
    }
    static TreeNode lca(TreeNode root, TreeNode p, TreeNode q) {
        // General binary tree; BOTH node references must exist in the tree.
        if (root == null || root == p || root == q) return root;
        TreeNode left = lca(root.left, p, q), right = lca(root.right, p, q);
        if (left != null && right != null) return root;
        return left != null ? left : right;
    }
    // Each O(n) time, O(h) call stack. Skewed trees can overflow Java's stack.
    // END

    // BEGIN 22 | Validate a BST with bounds | Essential
    static boolean validBST(TreeNode root) {
        return validBST(root, Long.MIN_VALUE, Long.MAX_VALUE);
    }
    static boolean validBST(TreeNode node, long low, long high) {
        if (node == null) return true;
        if (node.val <= low || node.val >= high) return false;
        return validBST(node.left, low, node.val) && validBST(node.right, node.val, high);
    }
    // Strict BST: duplicates disallowed. O(n), O(h). Long bounds admit all int values.
    // END

    // BEGIN 23 | Graph construction DFS and BFS distances | Essential
    static List<List<Integer>> graph(int n, int[][] edges, boolean directed) {
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
        for (int[] edge : edges) {
            graph.get(edge[0]).add(edge[1]);
            if (!directed) graph.get(edge[1]).add(edge[0]);
        }
        return graph; // vertex IDs 0..n-1
    }
    static int[] bfsDistances(List<List<Integer>> graph, int source) {
        int[] distance = new int[graph.size()];
        Arrays.fill(distance, -1);
        Queue<Integer> queue = new ArrayDeque<>();
        distance[source] = 0;
        queue.offer(source);
        while (!queue.isEmpty()) {
            int u = queue.poll();
            for (int v : graph.get(u)) {
                if (distance[v] != -1) continue;
                distance[v] = distance[u] + 1; // mark when enqueuing
                queue.offer(v);
            }
        }
        return distance; // unweighted shortest paths; -1 means unreachable
    }
    static int connectedComponents(List<List<Integer>> graph) {
        // UNDIRECTED graph. This is not an SCC algorithm for directed graphs.
        boolean[] visited = new boolean[graph.size()];
        int components = 0;
        Deque<Integer> stack = new ArrayDeque<>();
        for (int start = 0; start < graph.size(); start++) {
            if (visited[start]) continue;
            components++;
            visited[start] = true;
            stack.push(start);
            while (!stack.isEmpty()) {
                int u = stack.pop();
                for (int v : graph.get(u)) {
                    if (!visited[v]) { visited[v] = true; stack.push(v); }
                }
            }
        }
        return components;
    }
    // Each traversal O(V+E) time, O(V) auxiliary space (excluding adjacency).
    // END

    // BEGIN 24 | Grid and multi-source BFS | Essential
    static int[][] distanceToZero(int[][] grid) {
        // Rectangular binary grid; all cells traversable. No zero => all -1.
        int rows = grid.length, cols = rows == 0 ? 0 : grid[0].length;
        int[][] dist = new int[rows][cols];
        Queue<int[]> queue = new ArrayDeque<>();
        for (int r = 0; r < rows; r++) {
            Arrays.fill(dist[r], -1);
            for (int c = 0; c < cols; c++) {
                if (grid[r][c] == 0) { dist[r][c] = 0; queue.offer(new int[]{r, c}); }
            }
        }
        int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
        while (!queue.isEmpty()) {
            int[] cell = queue.poll();
            for (int[] d : directions) {
                int r = cell[0] + d[0], c = cell[1] + d[1];
                if (r < 0 || r >= rows || c < 0 || c >= cols || dist[r][c] != -1) continue;
                dist[r][c] = dist[cell[0]][cell[1]] + 1;
                queue.offer(new int[]{r, c});
            }
        }
        return dist; // O(rows*cols) time and space; input unchanged
    }
    // END

    // BEGIN 25 | Topological sort and directed cycle detection | Essential
    static int[] topologicalSort(List<List<Integer>> graph) {
        int n = graph.size();
        int[] indegree = new int[n], order = new int[n];
        for (List<Integer> neighbors : graph) for (int v : neighbors) indegree[v]++;
        Queue<Integer> queue = new ArrayDeque<>();
        for (int u = 0; u < n; u++) if (indegree[u] == 0) queue.offer(u);
        int count = 0;
        while (!queue.isEmpty()) {
            int u = queue.poll();
            order[count++] = u;
            for (int v : graph.get(u)) if (--indegree[v] == 0) queue.offer(v);
        }
        return count == n ? order : new int[0];
        // Nonempty graph + empty result => cycle. Empty graph has valid empty order.
        // Edge u->v means u must precede v. O(V+E), O(V) auxiliary space.
    }
    // END

    // BEGIN 26 | Dijkstra with stale-entry skipping | Essential
    static class Edge {
        final int to, weight;
        Edge(int to, int weight) { this.to = to; this.weight = weight; }
    }
    static long[] dijkstra(List<List<Edge>> graph, int source) {
        // All weights >= 0; finite distances fit in long and are < Long.MAX_VALUE.
        long[] dist = new long[graph.size()];
        Arrays.fill(dist, Long.MAX_VALUE);
        PriorityQueue<long[]> heap = new PriorityQueue<>((a, b) -> Long.compare(a[0], b[0]));
        dist[source] = 0;
        heap.offer(new long[]{0, source}); // {distance, node}
        while (!heap.isEmpty()) {
            long[] current = heap.poll();
            long distance = current[0];
            int u = (int) current[1];
            if (distance != dist[u]) continue;
            for (Edge edge : graph.get(u)) {
                long candidate = distance + edge.weight;
                if (candidate < dist[edge.to]) {
                    dist[edge.to] = candidate;
                    heap.offer(new long[]{candidate, edge.to});
                }
            }
        }
        return dist; // Long.MAX_VALUE means unreachable.
        // Lazy heap: O(V + E log(E+1)) time, O(V+E) auxiliary space.
    }
    // END

    // BEGIN 27 | Union-find with size and path compression | Essential
    static class UnionFind {
        final int[] parent, size;
        int components;
        UnionFind(int n) {
            parent = new int[n]; size = new int[n]; components = n;
            for (int i = 0; i < n; i++) { parent[i] = i; size[i] = 1; }
        }
        int find(int x) {
            while (x != parent[x]) { parent[x] = parent[parent[x]]; x = parent[x]; }
            return x;
        }
        boolean union(int a, int b) {
            int ra = find(a), rb = find(b);
            if (ra == rb) return false;
            if (size[ra] < size[rb]) { int temp = ra; ra = rb; rb = temp; }
            parent[rb] = ra;
            size[ra] += size[rb];
            components--;
            return true;
        }
    }
    // Amortized O(alpha(n)) per operation, O(n) storage, O(n) initialization.
    // union returning false detects a redundant edge in an undirected graph.
    // END

    // BEGIN 28 | Trie insert search and prefix | Essential
    static class Trie {
        static class Node { Node[] next = new Node[26]; boolean word; }
        final Node root = new Node();
        void insert(String s) { // lowercase a-z only; supports empty word
            Node node = root;
            for (int i = 0; i < s.length(); i++) {
                int index = s.charAt(i) - 'a';
                if (node.next[index] == null) node.next[index] = new Node();
                node = node.next[index];
            }
            node.word = true;
        }
        Node walk(String s) {
            Node node = root;
            for (int i = 0; i < s.length(); i++) {
                node = node.next[s.charAt(i) - 'a'];
                if (node == null) return null;
            }
            return node;
        }
        boolean search(String s) { Node node = walk(s); return node != null && node.word; }
        boolean startsWith(String prefix) { return walk(prefix) != null; }
    }
    // O(length) per operation; space proportional to total inserted characters
    // with 26 child slots per node. Use a map for a sparse/larger alphabet.
    // END

    // BEGIN 29 | Backtracking subsets and unique permutations | Essential
    static List<List<Integer>> subsets(int[] a) { // assumes distinct input values
        List<List<Integer>> result = new ArrayList<>();
        subsets(a, 0, new ArrayList<>(), result);
        return result;
    }
    static void subsets(int[] a, int start, List<Integer> path, List<List<Integer>> result) {
        result.add(new ArrayList<>(path)); // copy, never add the mutable path itself
        for (int i = start; i < a.length; i++) {
            path.add(a[i]);
            subsets(a, i + 1, path, result);
            path.remove(path.size() - 1);
        }
    }
    static List<List<Integer>> uniquePermutations(int[] input) {
        int[] a = input.clone(); Arrays.sort(a);
        List<List<Integer>> result = new ArrayList<>();
        permute(a, new boolean[a.length], new ArrayList<>(), result);
        return result;
    }
    static void permute(int[] a, boolean[] used, List<Integer> path, List<List<Integer>> result) {
        if (path.size() == a.length) { result.add(new ArrayList<>(path)); return; }
        for (int i = 0; i < a.length; i++) {
            if (used[i] || (i > 0 && a[i] == a[i - 1] && !used[i - 1])) continue;
            used[i] = true; path.add(a[i]);
            permute(a, used, path, result);
            path.remove(path.size() - 1); used[i] = false;
        }
    }
    // Subsets O(n*2^n); permutations O(n*n!) worst case, including output copies.
    // O(n) auxiliary space excluding returned output. Empty input yields [[]].
    // END

    // BEGIN 30 | Bottom-up DP coin change | Essential
    static int coinChange(int[] coins, int amount) {
        // Positive coin values, unlimited reuse, 0 <= amount < Integer.MAX_VALUE;
        // amount must be small enough to allocate the DP array.
        int impossible = amount + 1;
        int[] dp = new int[amount + 1];
        Arrays.fill(dp, impossible); dp[0] = 0;
        for (int value = 1; value <= amount; value++) {
            for (int coin : coins) {
                if (coin <= value && dp[value - coin] != impossible)
                    dp[value] = Math.min(dp[value], dp[value - coin] + 1);
            }
        }
        return dp[amount] == impossible ? -1 : dp[amount];
        // dp[value] = minimum coins for exactly value. O(amount*coins), O(amount).
        // 0/1 knapsack compresses capacity DESCENDING to prevent item reuse.
    }
    // END

    // BEGIN 31 | Two-dimensional DP longest common subsequence | Essential
    static int lcs(String a, String b) {
        int[][] dp = new int[a.length() + 1][b.length() + 1];
        for (int i = 1; i <= a.length(); i++) {
            for (int j = 1; j <= b.length(); j++) {
                if (a.charAt(i - 1) == b.charAt(j - 1)) dp[i][j] = 1 + dp[i - 1][j - 1];
                else dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
        return dp[a.length()][b.length()]; // O(m*n) time and space
        // dp[i][j] refers to prefixes of lengths i and j, not character indices.
    }
    // END

    // BEGIN 32 | Top-down memoization | Essential
    static long minPathSum(int[][] grid) {
        // Nonempty rectangular grid, move only right/down; negative costs allowed.
        return minPathSum(grid, 0, 0, new Long[grid.length][grid[0].length]);
    }
    static long minPathSum(int[][] g, int r, int c, Long[][] memo) {
        if (memo[r][c] != null) return memo[r][c];
        if (r == g.length - 1 && c == g[0].length - 1) return memo[r][c] = (long) g[r][c];
        long best = Long.MAX_VALUE;
        if (r + 1 < g.length) best = Math.min(best, minPathSum(g, r + 1, c, memo));
        if (c + 1 < g[0].length) best = Math.min(best, minPathSum(g, r, c + 1, memo));
        return memo[r][c] = g[r][c] + best;
    }
    // O(rows*cols) time/memo storage; O(rows+cols) recursion depth.
    // null means uncomputed: -1 is unsafe as a sentinel when valid results include -1.
    // END

    // BEGIN 33 | Longest increasing subsequence | Important
    static int lisLength(int[] a) {
        int[] tails = new int[a.length];
        int size = 0;
        for (int x : a) {
            int left = 0, right = size;
            while (left < right) {
                int mid = left + (right - left) / 2;
                if (tails[mid] < x) left = mid + 1;
                else right = mid;
            }
            tails[left] = x;
            if (left == size) size++;
        }
        return size; // strictly increasing; O(n log n), O(n)
        // tails[i] is the smallest known tail for length i+1; NOT the actual LIS.
    }
    // END

    // BEGIN 34 | Merge sort | Important
    static void mergeSort(int[] a) { mergeSort(a, new int[a.length], 0, a.length); }
    static void mergeSort(int[] a, int[] temp, int left, int right) {
        if (right - left <= 1) return;
        int mid = left + (right - left) / 2;
        mergeSort(a, temp, left, mid); mergeSort(a, temp, mid, right);
        int i = left, j = mid, write = left;
        while (i < mid && j < right) temp[write++] = a[i] <= a[j] ? a[i++] : a[j++];
        while (i < mid) temp[write++] = a[i++];
        while (j < right) temp[write++] = a[j++];
        for (int k = left; k < right; k++) a[k] = temp[k];
    }
    // [left,right); stable; mutates input. O(n log n), O(n) auxiliary space.
    // END

    // BEGIN 35 | Randomized three-way quickselect | Stretch
    static int kthSmallest(int[] a, int k) {
        // 1-based k; MUTATES input. Three-way partition handles duplicate pivots.
        if (k < 1 || k > a.length) throw new IllegalArgumentException("k");
        Random random = new Random();
        int target = k - 1, left = 0, right = a.length - 1;
        while (left <= right) {
            int pivot = a[left + random.nextInt(right - left + 1)];
            int less = left, scan = left, greater = right;
            while (scan <= greater) {
                if (a[scan] < pivot) swap(a, less++, scan++);
                else if (a[scan] > pivot) swap(a, scan, greater--);
                else scan++;
            }
            if (target < less) right = less - 1;
            else if (target > greater) left = greater + 1;
            else return pivot;
        }
        throw new AssertionError("unreachable");
    }
    static void swap(int[] a, int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; }
    // Expected O(n), worst O(n^2), O(1) auxiliary space.
    // END

    // BEGIN 36 | Fenwick tree for point updates and range sums | Stretch
    static class Fenwick {
        final long[] tree;
        Fenwick(int n) { tree = new long[n + 1]; }
        void add(int index, long delta) { // external zero-based index
            if (index < 0 || index >= tree.length - 1) throw new IndexOutOfBoundsException();
            for (int i = index + 1; i < tree.length; i += i & -i) tree[i] += delta;
        }
        long prefix(int end) { // sum of [0,end)
            if (end < 0 || end >= tree.length) throw new IndexOutOfBoundsException();
            long sum = 0;
            for (int i = end; i > 0; i -= i & -i) sum += tree[i];
            return sum;
        }
        long range(int left, int right) { return prefix(right) - prefix(left); }
    }
    // O(log n) per add/query, O(n) storage. Valid range: 0 <= left <= right <= n.
    // END

    // BEGIN 37 | LRU cache using library primitives | Important
    static class LruCache {
        final int capacity;
        final LinkedHashMap<Integer, Integer> map = new LinkedHashMap<>(16, 0.75f, true);
        LruCache(int capacity) {
            if (capacity < 0) throw new IllegalArgumentException("capacity");
            this.capacity = capacity;
        }
        Integer get(int key) { return map.get(key); } // null if absent; refreshes recency
        void put(int key, int value) {
            map.put(key, value);
            if (map.size() > capacity) {
                Iterator<Integer> iterator = map.keySet().iterator();
                iterator.next(); iterator.remove(); // least recently accessed
            }
        }
    }
    // Expected O(1) get/put, O(capacity) storage; not thread-safe.
    // If asked to implement internals, explain/map out hash map + doubly linked list.
    // END

    // BEGIN 38 | Immutable composite hash-map key | Important
    static final class Cell {
        final int row, col;
        Cell(int row, int col) { this.row = row; this.col = col; }
        @Override public boolean equals(Object other) {
            if (this == other) return true;
            if (!(other instanceof Cell)) return false;
            Cell cell = (Cell) other;
            return row == cell.row && col == cell.col;
        }
        @Override public int hashCode() { return 31 * row + col; }
    }
    // Set<Cell> cells = new HashSet<>(); cells.add(new Cell(1,2));
    // cells.contains(new Cell(1,2)) is true. int[] keys instead use identity equality.
    // Hash collisions are allowed; equal objects MUST have equal hash codes.
    // END

    // BEGIN 39 | Kadane maximum subarray | Essential
    static long maxSubarray(int[] a) {
        if (a.length == 0) throw new IllegalArgumentException("nonempty array required");
        long endingHere = a[0], best = a[0];
        for (int i = 1; i < a.length; i++) {
            endingHere = Math.max((long) a[i], endingHere + a[i]);
            best = Math.max(best, endingHere);
        }
        return best; // nonempty subarray; O(n), O(1); handles all-negative input
    }
    // END

    // BEGIN 40 | One-dimensional 0-1 knapsack | Important
    static long knapsack(int[] weights, int[] values, int capacity) {
        // Same-length arrays; weights > 0; capacity >= 0; choosing no items allowed.
        long[] dp = new long[capacity + 1];
        for (int i = 0; i < weights.length; i++) {
            for (int room = capacity; room >= weights[i]; room--) {
                dp[room] = Math.max(dp[room], dp[room - weights[i]] + values[i]);
            }
        }
        return dp[capacity]; // O(items*capacity), O(capacity)
        // Descending room prevents reusing this item; ascending would allow reuse.
    }
    // END

    // BEGIN 41 | LRU internals hash map and doubly linked list | Important
    static class ManualLruCache {
        static class Node {
            final int key;
            int value;
            Node prev, next;
            Node(int key, int value) { this.key = key; this.value = value; }
        }
        final int capacity;
        final Map<Integer, Node> map = new HashMap<>();
        final Node head = new Node(0, 0), tail = new Node(0, 0);
        ManualLruCache(int capacity) {
            if (capacity < 0) throw new IllegalArgumentException("capacity");
            this.capacity = capacity;
            head.next = tail; tail.prev = head;
        }
        void unlink(Node node) {
            node.prev.next = node.next;
            node.next.prev = node.prev;
        }
        void addMostRecent(Node node) { // insert immediately after head sentinel
            node.prev = head; node.next = head.next;
            head.next.prev = node; head.next = node;
        }
        Integer get(int key) {
            Node node = map.get(key);
            if (node == null) return null;
            unlink(node); addMostRecent(node);
            return node.value;
        }
        void put(int key, int value) {
            Node node = map.get(key);
            if (node != null) { node.value = value; unlink(node); }
            else { node = new Node(key, value); map.put(key, node); }
            addMostRecent(node);
            if (map.size() > capacity) {
                Node victim = tail.prev;
                unlink(victim); map.remove(victim.key);
            }
        }
    }
    // Expected O(1) per get/put, O(capacity) space; zero capacity works.
    // Invariant: every map node appears once between head and tail; most recent first.
    // END

    // BEGIN 42 | Bipartite graph coloring | Important
    static boolean isBipartite(List<List<Integer>> graph) {
        // Undirected graph; handles disconnected components and self-loops.
        int[] color = new int[graph.size()]; // 0 unvisited, +1/-1 colors
        Queue<Integer> queue = new ArrayDeque<>();
        for (int start = 0; start < graph.size(); start++) {
            if (color[start] != 0) continue;
            color[start] = 1; queue.offer(start);
            while (!queue.isEmpty()) {
                int u = queue.poll();
                for (int v : graph.get(u)) {
                    if (color[v] == color[u]) return false;
                    if (color[v] == 0) { color[v] = -color[u]; queue.offer(v); }
                }
            }
        }
        return true; // O(V+E) time, O(V) auxiliary space
    }
    // END
}

Outline

Keyboard shortcuts

/ or ⌘K
Search everything
j / k
Next / previous section
t
Table of contents
b
Bookmark the current section
?
Show this help

Try “select_for_update”, “Union-Find”, “@Transactional” or “STAR”.

↑ ↓ to move · ↵ to open · esc to close