Hide internal state behind a controlled API so callers cannot violate invariants. In Java: `private` fields + public methods; prefer immutable value objects for money/IDs.
LB distributes across instances (L4/L7; RR, least-conn, consistent hash). Reverse proxy fronts origins for TLS/cache/security — often the same box wears both hats (Nginx/Envoy).
Forward proxy?
Sits with the client for egress privacy/control/caching — opposite direction of reverse proxy.
API gateway role?
Auth, rate limit, routing, versioning at the API edge. Keep deep business invariants in domain services.
Routine → Rolling. Risky infra → Blue-Green. New features → Canary. Experiments → Feature toggles (deploy without exposing). Match pattern to risk — production fails from how change arrives.
18 animated: D1 Valid Parenthesis String with Wildcard, D2 Decode Ways, D3 Daily Temperatures, D4 Number of Islands, D13 Best Time to Buy and Sell Stock, D14 Coin Change, D15 Course Schedule, D16 3Sum, D17 Longest Substring Without Repeating Characters, D18 Merge Intervals, D20 Spiral Matrix, D23 Sliding Window Maximum, D24 Valid Parentheses, D30 Word Ladder, D31 Rotting Oranges, D34 Subarray Sum Equals K, D36 Jump Game II, D37 Trapping Rain Water. All 138 DSA problems (D1–D44 + D45–D94 + D95–D134 + D135–D138 Job Prep DP) include an Interactive Lab: code walkthrough, editable JS runner, and ≥10 tests.
D1. Valid Parenthesis String with Wildcard
O(n) time and O(1) auxiliary space.Greedily track the minimum and maximum possible unmatched opening counts.Animated
Given a string containing (, ), and , decide whether can be replaced by (, ), or empty so the result is valid.
Approach
Greedily track the minimum and maximum possible unmatched opening counts. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Track low/high = min/max feasible unmatched '(' count; both start at 0.
For each char: '(' increments both; ')' decrements both (clamp low to ≥0); '*' decrements low (clamped) and increments high.
If high < 0 at any point, return false (too many closes).
At the end, return whether low == 0 (zero unmatched opens is feasible).
Code
import java.util.*;
/**
* Main class to demonstrate the valid parentheses string checker with wildcards.
* The Solution class contains the algorithm that tracks min/max possible open parentheses.
*/
public class Main {
/**
* Solution class implementing the greedy algorithm for checking valid parentheses strings.
* '*' can be treated as '(', ')', or empty string.
* We track low (minimum possible open count) and high (maximum possible open count).
*/
static class Solution {
public boolean checkValidString(String s) {
int low = 0, high = 0; // min/max possible open parens
for (char c : s.toCharArray()) {
if (c == '(') { low++; high++; }
else if (c == ')') { low = Math.max(0, low - 1); high--; }
else { low = Math.max(0, low - 1); high++; } // * as ) or ( or empty
if (high < 0) return false;
}
return low == 0;
}
}
public static void main(String[] args) {
Solution solver = new Solution();
// Test cases covering various scenarios
String[] testCases = {
"()", // valid
"(*)", // valid: * as empty
"(*))", // valid: * as (
"(()*", // valid: * as )
")(", // invalid: starts with )
"((*", // invalid: too many opens
"*", // valid: * as empty
"**", // valid: both as empty
"(**)", // valid
"(*()" // valid: * as )
};
for (String test : testCases) {
boolean result = solver.checkValidString(test);
System.out.println("Input: \"" + test + "\" -> Valid: " + result);
}
}
}
Walkthrough
I will avoid enumerating star assignments. For each prefix I keep the smallest and largest possible open count. A close decreases both, an open increases both, and a star widens the range. If the maximum becomes negative no assignment works. At the end, zero must be in the feasible range. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty input; only stars; an early unmatched ); too many trailing opens.
Watch outs: Tracking one balance; forgetting to clamp low to zero; using exponential backtracking.
Ready
D2. Decode Ways
O(n) time and O(1) auxiliary space.Use prefix DP, retaining only the previous two states.Animated
Count how many ways a digit string can be decoded using 1 -> A through 26 -> Z.
Approach
Use prefix DP, retaining only the previous two states. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
If the string is empty or starts with '0', return 0.
Keep two rolling counts: prev2 = ways for prefix ending 2 ago, prev1 = ways for previous prefix; start both at 1.
For each position i=2..n: add prev1 if the 1-digit code is 1–9; add prev2 if the 2-digit code is 10–26.
Slide the window (prev2←prev1, prev1←curr) and return prev1.
Code
import java.util.*;
/**
* Main class to demonstrate the optimal decoding count algorithm.
* Contains the Solution class from the course snippet and a main method to test it.
*/
public class Main {
/**
* Solution class implementing the optimal O(n) time, O(1) space algorithm
* for counting the number of ways to decode a digit string.
*/
static class Solution {
public int numDecodingsOptimal(String s) {
if (s.isEmpty() || s.charAt(0) == '0') return 0;
int n = s.length();
int prev2 = 1, prev1 = 1;
for (int i = 2; i <= n; i++) {
int curr = 0;
int one = s.charAt(i - 1) - '0';
int two = 10 * (s.charAt(i - 2) - '0') + one;
if (one >= 1) curr += prev1;
if (two >= 10 && two <= 26) curr += prev2;
prev2 = prev1;
prev1 = curr;
}
return prev1;
}
}
public static void main(String[] args) {
Solution sol = new Solution();
// Test cases from typical decode ways problem
String[] tests = {
"12", // 2 ways: "AB" (1 2) or "L" (12)
"226", // 3 ways: "BBF", "BZ", "VF"
"06", // 0 ways: leading zero invalid
"10", // 1 way: "J"
"27", // 1 way: "BG" (2 7) because 27 > 26
"11106", // 2 ways: "AAJF", "KJF"
"", // 0 ways: empty string
"0", // 0 ways: single zero
"1", // 1 way: "A"
"101" // 1 way: "JA"
};
for (String t : tests) {
int ways = sol.numDecodingsOptimal(t);
System.out.println("Input: \"" + t + "\" -> Ways: " + ways);
}
}
}
Walkthrough
I define the state as ways to decode the processed prefix. At each position, I add the previous state for a nonzero single digit and the state two positions back for a number from 10 to 26. Only two prior values are needed, yielding linear time and constant space. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty string; leading zero; 10, 20; embedded zeros; long input and integer limits.
O(n) time and O(n) space.Maintain a decreasing stack of unresolved indices.Animated
For each daily temperature, return how many days must pass before a warmer temperature, or zero if none exists.
Approach
Maintain a decreasing stack of unresolved indices. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Allocate answer[n] filled with 0; keep a stack of unresolved indices (decreasing temperatures).
Scan left→right; while stack is nonempty and today's temp is warmer than temps[stack.top], pop and set answer[popped] = i - popped.
Push the current index onto the stack.
Indices never popped stay 0 (no warmer day). Return answer.
Code
import java.util.*;
/**
* Main class containing the Daily Temperatures solution and a demo driver.
*/
public class Main {
/**
* Solution for LeetCode 739 Daily Temperatures.
* Uses a monotonic decreasing stack of indices to find the next warmer day for each index.
*/
static class Solution {
/**
* Returns an array where each element is the number of days until a warmer temperature.
* If no warmer day exists, the value is 0.
* @param T array of daily temperatures
* @return array of days until warmer temperature
*/
public int[] dailyTemperatures(int[] T) {
int n = T.length;
int[] ans = new int[n];
Deque<Integer> stack = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && T[i] > T[stack.peek()]) {
int idx = stack.pop();
ans[idx] = i - idx;
}
stack.push(i);
}
return ans;
}
}
/**
* Demo driver: runs the solution on a few sample temperature arrays and prints results.
*/
public static void main(String[] args) {
Solution sol = new Solution();
// Sample 1: typical case with varying temperatures
int[] temps1 = {73, 74, 75, 71, 69, 72, 76, 73};
int[] result1 = sol.dailyTemperatures(temps1);
System.out.print("Input: "); printArray(temps1);
System.out.print("Output: "); printArray(result1);
System.out.println("Expected: [1, 1, 4, 2, 1, 1, 0, 0]");
System.out.println();
// Sample 2: strictly increasing temperatures
int[] temps2 = {30, 40, 50, 60};
int[] result2 = sol.dailyTemperatures(temps2);
System.out.print("Input: "); printArray(temps2);
System.out.print("Output: "); printArray(result2);
System.out.println("Expected: [1, 1, 1, 0]");
System.out.println();
// Sample 3: strictly decreasing temperatures (no warmer days)
int[] temps3 = {90, 80, 70, 60};
int[] result3 = sol.dailyTemperatures(temps3);
System.out.print("Input: "); printArray(temps3);
System.out.print("Output: "); printArray(result3);
System.out.println("Expected: [0, 0, 0, 0]");
System.out.println();
// Sample 4: single element
int[] temps4 = {100};
int[] result4 = sol.dailyTemperatures(temps4);
System.out.print("Input: "); printArray(temps4);
System.out.print("Output: "); printArray(result4);
System.out.println("Expected: [0]");
}
/**
* Helper to print an int array in a readable format.
*/
private static void printArray(int[] arr) {
System.out.print("[");
for (int i = 0; i < arr.length; i++) {
if (i > 0) System.out.print(", ");
System.out.print(arr[i]);
}
System.out.println("]");
}
}
Walkthrough
I keep indices whose warmer day is not known. When today's temperature exceeds the stack top, today is the first warmer day for that index, so I pop and record the distance. Each index is pushed and popped once, making the algorithm linear. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty array; equal temperatures; strictly increasing or decreasing values.
Watch outs: Pushing values instead of indices; treating equal as warmer; assuming nested loops imply quadratic despite one push/pop each.
Ready
D4. Number of Islands
O(rows cols) time and O(rows cols) worst-case recursion/queue space.Start DFS or BFS at each unvisited land cell and mark its whole component.Animated
Count connected components of land in a 0/1 grid using four-directional adjacency. Naming islands is a labeling extension.
Approach
Start DFS or BFS at each unvisited land cell and mark its whole component. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Scan every cell; when you see land '1', increment the island count.
DFS/BFS from that cell: mark the cell as water '0' (or visited) and recurse/enqueue all 4-neighbors that are land.
Continue until the whole connected component is marked.
Return the island count after the full grid scan.
Code
/*
* Main class to demonstrate the number of islands algorithm.
* This program creates sample grids and uses the Solution class
* to count the number of connected land components ('1's).
*/
import java.util.Arrays;
public class Main {
/*
* Solution class containing the island counting algorithm.
* Uses depth-first search (DFS) to mark visited land cells.
*/
static class Solution {
/**
* Counts the number of islands in the grid.
* An island is a group of horizontally or vertically adjacent '1's.
* @param grid 2D char array representing the map ('1' = land, '0' = water)
* @return number of distinct islands
*/
public int numIslands(char[][] grid) {
if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;
int islands = 0;
for (int r = 0; r < grid.length; r++)
for (int c = 0; c < grid[0].length; c++)
if (grid[r][c] == '1') {
islands++;
sink(grid, r, c);
}
return islands;
}
/**
* Recursive DFS helper that marks all connected land as water ('0').
* This effectively "sinks" the island to avoid recounting.
* @param grid the map being modified in-place
* @param r current row
* @param c current column
*/
private void sink(char[][] grid, int r, int c) {
if (r < 0 || r == grid.length || c < 0 || c == grid[0].length
|| grid[r][c] != '1') return;
grid[r][c] = '0';
sink(grid, r + 1, c); sink(grid, r - 1, c);
sink(grid, r, c + 1); sink(grid, r, c - 1);
}
}
public static void main(String[] args) {
Solution solver = new Solution();
// Test case 1: Simple grid with 1 island
char[][] grid1 = {
{'1','1','1','1','0'},
{'1','1','0','1','0'},
{'1','1','0','0','0'},
{'0','0','0','0','0'}
};
System.out.println("Grid 1:");
printGrid(grid1);
System.out.println("Number of islands: " + solver.numIslands(grid1)); // Expected: 1
System.out.println();
// Test case 2: Grid with 3 islands
char[][] grid2 = {
{'1','1','0','0','0'},
{'1','1','0','0','0'},
{'0','0','1','0','0'},
{'0','0','0','1','1'}
};
System.out.println("Grid 2:");
printGrid(grid2);
System.out.println("Number of islands: " + solver.numIslands(grid2)); // Expected: 3
System.out.println();
// Test case 3: Empty grid
char[][] grid3 = {};
System.out.println("Grid 3 (empty):");
System.out.println("Number of islands: " + solver.numIslands(grid3)); // Expected: 0
System.out.println();
// Test case 4: All water
char[][] grid4 = {
{'0','0','0'},
{'0','0','0'}
};
System.out.println("Grid 4 (all water):");
printGrid(grid4);
System.out.println("Number of islands: " + solver.numIslands(grid4)); // Expected: 0
System.out.println();
// Test case 5: Single cell land
char[][] grid5 = {{'1'}};
System.out.println("Grid 5 (single land cell):");
printGrid(grid5);
System.out.println("Number of islands: " + solver.numIslands(grid5)); // Expected: 1
}
/**
* Helper method to print a 2D char grid for visualization.
* @param grid the grid to print
*/
private static void printGrid(char[][] grid) {
if (grid == null || grid.length == 0) {
System.out.println(" (empty)");
return;
}
for (char[] row : grid) {
System.out.print(" ");
for (char cell : row) {
System.out.print(cell + " ");
}
System.out.println();
}
}
}
Walkthrough
I scan the grid and launch a traversal only from unvisited land. That traversal marks every cell in the same four-connected component. Therefore each island contributes exactly one launch, and because each cell is processed once, runtime is linear in grid size. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty grid; one cell; all water/land; jagged input if allowed; recursion depth.
Watch outs: Counting cells instead of components; diagonal adjacency; failing to mark before enqueueing.
Ready
D5. Meeting Rooms II
O(n log n) time and O(n) space for sorted endpoint arrays.Sort starts and ends independently, then sweep them with two pointers.
Given meeting intervals, return the minimum number of rooms required so no overlapping meetings share a room.
Approach
Sort starts and ends independently, then sweep them with two pointers. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Extract all start times and end times into two arrays; sort both ascending.
Two pointers i (starts), j (ends); rooms=0, peak=0.
If next start < next end: open a room (rooms++, update peak), i++; else free a room (rooms--), j++.
Return peak (maximum concurrent meetings).
Code
import java.util.*;
// Main class for the meeting rooms problem
// Main class containing the meeting rooms solution and test cases
public class Main {
// Solution class that calculates minimum meeting rooms needed
// Static nested class that implements the minimum meeting rooms algorithm
static class Solution {
// Method to calculate minimum meeting rooms needed for given intervals
public int minMeetingRooms(int[][] intervals) {
int n = intervals.length;
// Early return for empty input to avoid unnecessary processing
if (n == 0) return 0;
// Allocate arrays to hold meeting start and end times separately
int[] starts = new int[n], ends = new int[n];
// Extract start and end times from interval pairs into separate arrays
for (int i = 0; i < n; i++) {
starts[i] = intervals[i][0];
ends[i] = intervals[i][1];
}
// Sort both arrays to enable chronological sweep line processing
Arrays.sort(starts); Arrays.sort(ends);
// Initialize pointers for starts/ends and counters for active rooms and max needed
int start = 0, end = 0, active = 0, answer = 0;
// Process meetings in chronological order using two-pointer technique
while (start < n) {
// If next meeting starts before earliest ending meeting, need new room
if (starts[start] < ends[end]) {
// Increment active room count and track maximum rooms used simultaneously
answer = Math.max(answer, ++active);
start++;
// Otherwise a meeting has ended, free up a room
} else {
// Decrement active count and advance end pointer to next ending meeting
active--; end++;
}
}
// Return the peak number of concurrent meetings found
return answer;
}
}
// Main method to test the solution
// Entry point that runs predefined test cases to verify correctness
public static void main(String[] args) {
// Instantiate the solution class for testing
Solution solution = new Solution();
// Test case 1: Overlapping meetings
// Test case with three meetings where two overlap requiring two rooms
int[][] intervals1 = {{0, 30}, {5, 10}, {15, 20}};
// Execute test 1 and print result with expected value
System.out.println("Test 1: " + solution.minMeetingRooms(intervals1) + " rooms needed (expected 2)");
// Test case 2: Non-overlapping meetings
// Test case with two non-overlapping meetings needing only one room
int[][] intervals2 = {{7, 10}, {2, 4}};
// Execute test 2 and print result with expected value
System.out.println("Test 2: " + solution.minMeetingRooms(intervals2) + " rooms needed (expected 1)");
// Test case 3: Empty intervals
// Test case with empty input array expecting zero rooms
int[][] intervals3 = {};
// Execute test 3 and print result with expected value
System.out.println("Test 3: " + solution.minMeetingRooms(intervals3) + " rooms needed (expected 0)");
// Test case 4: Multiple overlapping
// Test case with four heavily overlapping meetings needing four rooms
int[][] intervals4 = {{1, 5}, {2, 6}, {3, 7}, {4, 8}};
// Execute test 4 and print result with expected value
System.out.println("Test 4: " + solution.minMeetingRooms(intervals4) + " rooms needed (expected 4)");
// Test case 5: Single meeting
// Test case with single meeting requiring exactly one room
int[][] intervals5 = {{10, 20}};
// Execute test 5 and print result with expected value
System.out.println("Test 5: " + solution.minMeetingRooms(intervals5) + " rooms needed (expected 1)");
}
}
Walkthrough
I separate and sort all starts and ends. If the next event is a start before the earliest end, one more room is active; otherwise a room is released. The peak active count is exactly the minimum number of rooms. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: No meetings; touching endpoints; duplicate intervals; zero-length meetings.
Watch outs: Using <= when a meeting ending at time t frees a room for a start at t; returning current rather than maximum rooms.
D6. LRU Cache
O(1) average per operation and O(capacity) space.Combine a key-to-node hash map with a doubly linked recency list.
Design get and put for a fixed-capacity least-recently-used cache in average O(1) time.
Approach
Combine a key-to-node hash map with a doubly linked recency list. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Store key→node in a HashMap; keep a doubly linked list ordered by recency (MRU at head).
get: if missing return miss; else move node to head and return value.
put: if key exists, update value and move to head; else insert at head.
If size exceeds capacity, evict the tail (LRU) and remove it from the map.
Code
import java.util.*;
/**
* Demonstrates the LRUCache: a sequence of put and get calls on a capacity-2 cache
* shows which entry is evicted each time.
*/
public class Main {
/**
* LRUCache implements a Least Recently Used cache with O(1) get and put.
* Uses a HashMap for key-to-node mapping and a doubly linked list to maintain recency order.
*/
static class LRUCache {
/**
* Node in the doubly linked list holding key, value, and prev/next pointers.
*/
private static final class Node {
int key, value; Node prev, next; // fields for key, value, and links
Node() {} // default constructor for dummy nodes
Node(int key, int value) { this.key = key; this.value = value; } // constructor with key and value
}
private final int capacity; // maximum number of entries
private final Map<Integer, Node> nodes = new HashMap<>(); // map from key to node
private final Node least = new Node(), most = new Node(); // dummy head (least recent) and tail (most recent)
LRUCache(int capacity) { // constructor
if (capacity < 0) throw new IllegalArgumentException("negative capacity"); // validate capacity
this.capacity = capacity;
least.next = most; most.prev = least; // initialize empty doubly linked list
}
/**
* Returns the value for key if present, otherwise -1. Moves accessed node to most-recent position.
*/
public int get(int key) {
Node node = nodes.get(key); // find node in map
if (node == null) return -1; // not found
remove(node); addMostRecent(node); // move to most-recent
return node.value; // return value
}
/**
* Inserts or updates key-value pair. If key exists, updates value and moves to most-recent.
* If new key and capacity exceeded, evicts least-recently-used entry.
*/
public void put(int key, int value) {
if (capacity == 0) return; // zero capacity, nothing to store
Node node = nodes.get(key); // check if key exists
if (node != null) {
node.value = value; remove(node); addMostRecent(node); return; // update and move to front
}
node = new Node(key, value); // create new node
nodes.put(key, node); addMostRecent(node); // add to map and list
if (nodes.size() > capacity) { // if over capacity
Node victim = least.next; // least-recently-used node
remove(victim); nodes.remove(victim.key); // remove from list and map
}
}
/**
* Removes a node from the doubly linked list.
*/
private void remove(Node node) {
node.prev.next = node.next; node.next.prev = node.prev; // unlink node
}
/**
* Adds a node to the most-recent position (just before dummy tail).
*/
private void addMostRecent(Node node) {
node.prev = most.prev; node.next = most; // link node with current most-recent and dummy tail
most.prev.next = node; most.prev = node; // update list pointers
}
}
public static void main(String[] args) {
LRUCache cache = new LRUCache(2); // capacity 2
// Put key-value pairs
cache.put(1, 1);
System.out.println("put(1, 1)");
cache.put(2, 2);
System.out.println("put(2, 2)");
// Get existing key
int val1 = cache.get(1);
System.out.println("get(1) = " + val1); // should be 1
// Put another pair, evicts LRU (key 2)
cache.put(3, 3);
System.out.println("put(3, 3)");
// Get evicted key
int val2 = cache.get(2);
System.out.println("get(2) = " + val2); // should be -1
// Put another, evicts LRU (key 1)
cache.put(4, 4);
System.out.println("put(4, 4)");
// Get evicted key
int val3 = cache.get(1);
System.out.println("get(1) = " + val3); // should be -1
// Get remaining keys
int val4 = cache.get(3);
System.out.println("get(3) = " + val4); // should be 3
int val5 = cache.get(4);
System.out.println("get(4) = " + val5); // should be 4
}
}
Walkthrough
The hash map gives O(1) lookup, while a doubly linked list gives O(1) removal and insertion. I keep least recent at one end and most recent at the other. Every hit or update moves a node to most recent; overflow removes the least-recent node from both structures. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Capacity one; updating an existing key; repeated gets; clarify whether capacity zero is legal.
Watch outs: Not moving updated keys; stale map entries after eviction; pointer bugs at list ends.
D7. LFU Cache
O(1) average per operation and O(capacity) space.Map keys to nodes and frequencies to ordered buckets; track the minimum frequency.
Design an O(1)-average cache that evicts the least frequently used key, breaking ties by least recency.
Approach
Map keys to nodes and frequencies to ordered buckets; track the minimum frequency. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Map key→node and freq→ordered bucket of keys at that frequency; track minFreq.
get/put hit: remove from old freq bucket, insert into freq+1, update minFreq if the old bucket emptied.
put miss at capacity: evict the least-recent key from the minFreq bucket, then insert the new key at freq 1.
New inserts set minFreq = 1. All ops O(1) amortized.
Code
import java.util.*;
/**
* Main class to demonstrate the LFU (Least Frequently Used) Cache implementation.
* This is a single-file runnable program for online compilers like JDoodle.
*/
public class Main {
/**
* LFUCache implements a Least Frequently Used cache with O(1) operations.
* It uses a hash map for key-to-node lookup and frequency-to-doubly-linked-list mapping.
* The minFreq variable tracks the minimum frequency for efficient eviction.
*/
static class LFUCache {
/** Node represents a cache entry with key, value, frequency, and doubly-linked list pointers. */
class Node {
int key, val, freq;
Node prev, next;
Node(int k, int v) { key = k; val = v; freq = 1; }
}
/** DoublyLinkedList maintains nodes of the same frequency in LRU order (head = most recent). */
class DoublyLinkedList {
Node head = new Node(0, 0), tail = new Node(0, 0);
int size = 0;
DoublyLinkedList() { head.next = tail; tail.prev = head; }
void addToHead(Node node) {
node.next = head.next; node.prev = head;
head.next.prev = node; head.next = node;
size++;
}
void remove(Node node) {
node.prev.next = node.next; node.next.prev = node.prev;
size--;
}
Node removeTail() {
if (size == 0) return null;
Node node = tail.prev;
remove(node);
return node;
}
}
private final int capacity;
private int minFreq;
private final Map<Integer, Node> keyToNode = new HashMap<>();
private final Map<Integer, DoublyLinkedList> freqToList = new HashMap<>();
public LFUCache(int capacity) { this.capacity = capacity; }
public int get(int key) {
if (!keyToNode.containsKey(key)) return -1;
Node node = keyToNode.get(key);
updateFreq(node);
return node.val;
}
public void put(int key, int value) {
if (capacity == 0) return;
if (keyToNode.containsKey(key)) {
Node node = keyToNode.get(key);
node.val = value;
updateFreq(node);
return;
}
if (keyToNode.size() >= capacity) {
DoublyLinkedList minList = freqToList.get(minFreq);
Node evicted = minList.removeTail();
keyToNode.remove(evicted.key);
}
Node node = new Node(key, value);
keyToNode.put(key, node);
freqToList.computeIfAbsent(1, k -> new DoublyLinkedList()).addToHead(node);
minFreq = 1;
}
private void updateFreq(Node node) {
int freq = node.freq;
DoublyLinkedList list = freqToList.get(freq);
list.remove(node);
if (list.size == 0 && freq == minFreq) minFreq++;
node.freq++;
freqToList.computeIfAbsent(node.freq, k -> new DoublyLinkedList()).addToHead(node);
}
}
/** Main method to exercise the LFUCache with sample operations and print results. */
public static void main(String[] args) {
// Create an LFU cache with capacity 2
LFUCache cache = new LFUCache(2);
// Put key-value pairs
cache.put(1, 10); // cache: {1=10}, freq: 1->[1]
cache.put(2, 20); // cache: {1=10, 2=20}, freq 1 list: [2, 1] (head = most recent)
// Get existing key - should return 10 and increment frequency of key 1
System.out.println("get(1): " + cache.get(1)); // Output: 10; key 1 moves to freq 2, freq 1 list: [2]
// Put new key when at capacity - should evict least frequently used (key 2, freq 1)
cache.put(3, 30); // evicts key 2 (freq 1); cache: {1=10, 3=30}
// Get evicted key - should return -1
System.out.println("get(2): " + cache.get(2)); // Output: -1
// Get existing keys
System.out.println("get(3): " + cache.get(3)); // Output: 30; key 3 moves to freq 2, freq 2 list: [3, 1]
System.out.println("get(1): " + cache.get(1)); // Output: 10; key 1 moves to freq 3 (alone), freq 2 list: [3]
// Put new key when at capacity - lowest frequency is 2 (key 3), so key 3 is evicted
cache.put(4, 40); // evicts key 3; cache: {1=10, 4=40}
System.out.println("get(3): " + cache.get(3)); // Output: -1 (evicted)
System.out.println("get(4): " + cache.get(4)); // Output: 40
System.out.println("get(1): " + cache.get(1)); // Output: 10
}
}
Walkthrough
I maintain direct key lookup plus an LRU-ordered bucket for every frequency. Access removes a node from frequency f and appends it to f+1. The smallest nonempty frequency is tracked explicitly, so eviction removes that bucket's least-recent node in constant time. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Capacity zero/one; frequency ties; updating existing values; emptying the minimum bucket.
O(n) time and O(1) space.Use a dummy head and repeatedly move the next node to the front of the target sublist.
Reverse a singly linked list from 1-indexed positions left through right in one pass.
Approach
Use a dummy head and repeatedly move the next node to the front of the target sublist. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Create a dummy before head; advance prev to the node before position left.
Let cur = prev.next (start of the sublist).
For right-left times: detach cur.next and splice it immediately after prev (head-insertion reverse).
Return dummy.next as the new head.
Code
import java.util.*;
/**
* Main class demonstrating the reverseBetween linked list algorithm.
* The Solution class contains the algorithm to reverse a sublist
* from position left to right (1-indexed) in a singly-linked list.
*/
public class Main {
/**
* Solution class containing the reverseBetween method.
* This is the original snippet class, kept exactly as provided.
*/
static class Solution {
/**
* ListNode represents a node in a singly-linked list.
* Contains an integer value and a reference to the next node.
*/
static class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
/**
* Reverses the nodes of the list from position left to position right.
* Uses a dummy node and head-insertion technique to reverse in-place.
* @param head The head of the linked list
* @param left The starting position (1-indexed) of the sublist to reverse
* @param right The ending position (1-indexed) of the sublist to reverse
* @return The new head of the modified list
*/
public ListNode reverseBetween(ListNode head, int left, int right) {
ListNode dummy = new ListNode(0, head); // Create dummy node before head
ListNode prev = dummy; // prev will point to node before left
for (int i = 0; i < left - 1; i++) prev = prev.next; // Advance prev to node before left
ListNode cur = prev.next; // cur is the first node of sublist (position left)
for (int i = 0; i < right - left; i++) { // Perform right-left iterations
ListNode next = cur.next; // next is the node to move to front
cur.next = next.next; // Detach next from cur
next.next = prev.next; // Point next to current front of sublist
prev.next = next; // Splice next after prev (new front)
}
return dummy.next; // Return new head (dummy.next)
}
}
/**
* Helper method to create a linked list from an array of values.
* @param values Array of integers to convert to linked list
* @return Head of the created linked list
*/
static Solution.ListNode createList(int[] values) {
if (values.length == 0) return null;
Solution.ListNode head = new Solution.ListNode(values[0]);
Solution.ListNode current = head;
for (int i = 1; i < values.length; i++) {
current.next = new Solution.ListNode(values[i]);
current = current.next;
}
return head;
}
/**
* Helper method to convert a linked list to a string representation.
* @param head Head of the linked list
* @return String like "1 -> 2 -> 3"
*/
static String listToString(Solution.ListNode head) {
StringBuilder sb = new StringBuilder();
while (head != null) {
sb.append(head.val);
if (head.next != null) sb.append(" -> ");
head = head.next;
}
return sb.toString();
}
/**
* Main method demonstrating the reverseBetween algorithm.
* Creates a sample list, reverses a sublist, and prints results.
*/
public static void main(String[] args) {
Solution solution = new Solution();
// Test case 1: Reverse sublist from position 2 to 4 in list 1->2->3->4->5
int[] values1 = {1, 2, 3, 4, 5};
Solution.ListNode head1 = createList(values1);
System.out.println("Original list: " + listToString(head1));
Solution.ListNode result1 = solution.reverseBetween(head1, 2, 4);
System.out.println("After reverseBetween(2, 4): " + listToString(result1));
System.out.println("Expected: 1 -> 4 -> 3 -> 2 -> 5");
System.out.println();
// Test case 2: Reverse entire list (left=1, right=length)
int[] values2 = {1, 2, 3, 4, 5};
Solution.ListNode head2 = createList(values2);
System.out.println("Original list: " + listToString(head2));
Solution.ListNode result2 = solution.reverseBetween(head2, 1, 5);
System.out.println("After reverseBetween(1, 5): " + listToString(result2));
System.out.println("Expected: 5 -> 4 -> 3 -> 2 -> 1");
System.out.println();
// Test case 3: Reverse single element (left == right)
int[] values3 = {1, 2, 3, 4, 5};
Solution.ListNode head3 = createList(values3);
System.out.println("Original list: " + listToString(head3));
Solution.ListNode result3 = solution.reverseBetween(head3, 3, 3);
System.out.println("After reverseBetween(3, 3): " + listToString(result3));
System.out.println("Expected: 1 -> 2 -> 3 -> 4 -> 5 (unchanged)");
System.out.println();
// Test case 4: Reverse from head to middle
int[] values4 = {1, 2, 3, 4, 5};
Solution.ListNode head4 = createList(values4);
System.out.println("Original list: " + listToString(head4));
Solution.ListNode result4 = solution.reverseBetween(head4, 1, 3);
System.out.println("After reverseBetween(1, 3): " + listToString(result4));
System.out.println("Expected: 3 -> 2 -> 1 -> 4 -> 5");
}
}
Walkthrough
A dummy node removes the special case where reversal starts at the head. I stop just before left, then repeatedly detach the node after the sublist tail and insert it at the sublist front. This reverses exactly the requested range in one pass and constant space. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Reversing from the head; left == right; right at tail; invalid positions if not guaranteed.
Watch outs: Losing the successor pointer; off-by-one traversal; returning the old head.
D9. Pair in Two BSTs with Target Sum
O(n + m) time and O(n + h) space, where h is traversal depth.Traverse one BST into a set, then search the other for complements.
Determine whether one value from each of two BSTs sums to a target.
Approach
Traverse one BST into a set, then search the other for complements. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
DFS/BFS one BST into a HashSet of values.
Traverse the second BST; for each node value x, check if target - x is in the set.
If found, return true (one node from each tree).
If the traversal finishes with no hit, return false.
Code
import java.util.*;
// Main class to demonstrate finding a target sum across two BSTs
public class Main {
// Solution class containing the algorithm to find if two nodes (one from each BST) sum to target
static class Solution {
public boolean findTarget(TreeNode first, TreeNode second, int target) {
Set<Integer> values = new HashSet<>();
collect(first, values);
return hasComplement(second, values, target);
}
private void collect(TreeNode node, Set<Integer> values) {
if (node == null) return;
values.add(node.val);
collect(node.left, values); collect(node.right, values);
}
private boolean hasComplement(TreeNode node, Set<Integer> values, int target) {
if (node == null) return false;
long complement = (long) target - node.val;
if (complement >= Integer.MIN_VALUE && complement <= Integer.MAX_VALUE
&& values.contains((int) complement)) return true;
return hasComplement(node.left, values, target)
|| hasComplement(node.right, values, target);
}
}
// TreeNode class representing a node in a binary search tree
static class TreeNode {
int val; TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
// Main method to test the findTarget algorithm with sample BSTs
public static void main(String[] args) {
// Build first BST: 5
// / \
// 3 7
TreeNode first = new TreeNode(5);
first.left = new TreeNode(3);
first.right = new TreeNode(7);
// Build second BST: 10
// / \
// 6 15
TreeNode second = new TreeNode(10);
second.left = new TreeNode(6);
second.right = new TreeNode(15);
Solution sol = new Solution();
// Test case 1: target = 13 (3 from first + 10 from second = 13)
System.out.println("Target 13: " + sol.findTarget(first, second, 13)); // true
// Test case 2: target = 20 (5 from first + 15 from second = 20)
System.out.println("Target 20: " + sol.findTarget(first, second, 20)); // true
// Test case 3: target = 100 (no pair sums to 100)
System.out.println("Target 100: " + sol.findTarget(first, second, 100)); // false
// Test case 4: target = 9 (3 from first + 6 from second = 9)
System.out.println("Target 9: " + sol.findTarget(first, second, 9)); // true
}
}
Walkthrough
I first index all values from one tree in a hash set. While traversing the second tree, I test whether target minus the current value is present. That checks every cross-tree pair implicitly in linear expected time. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Either tree null; duplicate values; negative values; skewed trees.
Watch outs: Searching the same tree twice; assuming distinct values; integer overflow for extreme values.
D10. Maximum Element Using Java Streams
O(n) time and O(1) reduction space.Use mapToInt(...).max() and preserve or unwrap the resulting optional.
Return the maximum integer in a list using the Java Stream API, with an explicit empty-input contract.
Approach
Use mapToInt(...).max() and preserve or unwrap the resulting optional. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Obtain the stream (list/collection).
Call mapToInt(...).max() (or stream().max(Comparator...) for objects).
Handle empty: orElseThrow / orElse per contract.
Return the unwrapped maximum.
Code
import java.util.*;
import java.util.stream.*;
/**
* Main class to demonstrate the Solution.max method which finds the maximum
* integer in a list using streams, returning an OptionalInt.
*/
public class Main {
/**
* Solution class containing the max method that computes the maximum value
* from a list of integers using Java streams.
*/
static class Solution {
/**
* Returns an OptionalInt containing the maximum value in the list,
* or an empty OptionalInt if the list is empty.
* Throws NullPointerException if the list or any element is null.
*/
public OptionalInt max(List<Integer> values) {
Objects.requireNonNull(values, "values"); // ensure list is not null
return values.stream() // create stream from list
.mapToInt(value -> Objects.requireNonNull(value, "null element")) // map to int, check nulls
.max(); // find maximum, returns OptionalInt
}
}
public static void main(String[] args) {
Solution solution = new Solution(); // create solution instance
// Demo with non-empty list
List<Integer> nonEmpty = Arrays.asList(3, 1, 4, 1, 5, 9);
OptionalInt result1 = solution.max(nonEmpty); // find max
System.out.println("Non-empty list: " + nonEmpty); // print input
if (result1.isPresent()) { // check if max exists
System.out.println("Max: " + result1.getAsInt()); // print max value
} else {
System.out.println("Max: (empty)"); // shouldn't happen
}
// Demo with empty list
List<Integer> empty = Collections.emptyList(); // empty list
OptionalInt result2 = solution.max(empty); // find max on empty
System.out.println("Empty list: " + empty); // print input
if (result2.isPresent()) { // check if max exists
System.out.println("Max: " + result2.getAsInt()); // print max value
} else {
System.out.println("Max: (empty)"); // expected for empty list
}
}
}
Walkthrough
I would clarify empty and null behavior first. Then I map boxed integers to an IntStream and use its max reduction. The result is OptionalInt because no identity value can correctly represent the maximum of every possible integer list. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Watch outs: Calling getAsInt() without an empty contract; using reduce(0, max) for negative input; hidden unboxing nulls.
D11. Range Sum Query with Updates (Segment Tree)
Build O(n); update O(log n); query O(log n); space O(n).Store interval sums in a segment tree and update/query only intersecting branches.
Canonical range-query choice: support point updates and inclusive range-sum queries on an integer array.
Approach
Store interval sums in a segment tree and update/query only intersecting branches. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Build a segment tree over the array where each node stores the sum of its interval.
update(i, val): walk to the leaf for i, set it, and recompute ancestors upward.
import java.util.*;
/**
* Main class demonstrating the SegmentTree implementation.
* SegmentTree provides O(log n) range sum queries and point updates.
*/
public class Main {
/**
* SegmentTree stores interval sums in a compact array (size 2*n).
* Leaves are at indices [n, 2n-1], internal nodes at [1, n-1].
* update(index, value) walks up from leaf to root recomputing sums.
* query(left, right) climbs the tree adding fully covered nodes.
*/
static class SegmentTree {
private final int n;
private final long[] tree;
SegmentTree(int[] values) {
if (values == null || values.length == 0)
throw new IllegalArgumentException("values must be nonempty");
n = values.length;
tree = new long[2 * n];
for (int i = 0; i < n; i++) tree[n + i] = values[i]; // copy values into leaves
for (int i = n - 1; i > 0; i--) tree[i] = tree[i << 1] + tree[i << 1 | 1]; // build internal nodes bottom-up
}
public void update(int index, int value) {
checkIndex(index);
int p = index + n;
tree[p] = value; // set leaf
while ((p >>= 1) > 0) tree[p] = tree[p << 1] + tree[p << 1 | 1]; // recompute ancestors
}
public long query(int left, int right) {
if (left < 0 || right < left || right >= n) throw new IndexOutOfBoundsException();
long sum = 0;
for (left += n, right += n + 1; left < right; left >>= 1, right >>= 1) {
if ((left & 1) == 1) sum += tree[left++]; // left is right child, include and move right
if ((right & 1) == 1) sum += tree[--right]; // right is left child, include and move left
}
return sum;
}
private void checkIndex(int index) {
if (index < 0 || index >= n) throw new IndexOutOfBoundsException(index);
}
}
public static void main(String[] args) {
// Sample array for demonstration
int[] arr = {1, 3, 5, 7, 9, 11};
SegmentTree st = new SegmentTree(arr);
// Query sum of entire array: 1+3+5+7+9+11 = 36
System.out.println("Sum [0,5] = " + st.query(0, 5));
// Query sub-range: 5+7+9 = 21
System.out.println("Sum [2,4] = " + st.query(2, 4));
// Update index 2 from 5 to 10
st.update(2, 10);
System.out.println("After update index 2 to 10:");
System.out.println("Sum [0,5] = " + st.query(0, 5)); // 1+3+10+7+9+11 = 41
System.out.println("Sum [2,4] = " + st.query(2, 4)); // 10+7+9 = 26
}
}
Walkthrough
I model each node as the sum of a fixed interval. A point update changes one leaf and recomputes its ancestors. A query returns a node immediately when fully covered, ignores disjoint nodes, and combines partial overlaps. The tree height makes both operations logarithmic. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty array contract; single element; full range; repeated updates; sum overflow.
Watch outs: Mixing inclusive and half-open ranges; returning wrong identity for no overlap; forgetting parent recomputation.
D12. Mirror Image of Binary Tree
O(n) time and O(h) recursion space.Recursively invert both subtrees and swap their roots.
Invert a binary tree by swapping the left and right child of every node.
Approach
Recursively invert both subtrees and swap their roots. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
If root is null, return null.
Recursively mirror the left subtree and the right subtree.
Swap root.left and root.right.
Return root (in-place) or a newly built mirrored copy if the contract forbids mutation.
Code
import java.util.*;
// Main class to run the tree inversion demo
public class Main {
// Solution class containing the tree inversion algorithm
static class Solution {
// TreeNode represents a node in a binary tree
static class TreeNode { int val; TreeNode left, right; TreeNode(int v) { val=v; } }
// Recursively inverts a binary tree by swapping left and right children
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
}
}
// Helper method to print tree in level order for visualization
static void printTree(Solution.TreeNode root) {
if (root == null) {
System.out.println("[]");
return;
}
Queue<Solution.TreeNode> queue = new LinkedList<>();
queue.offer(root);
List<String> values = new ArrayList<>();
while (!queue.isEmpty()) {
Solution.TreeNode node = queue.poll();
if (node != null) {
values.add(String.valueOf(node.val));
queue.offer(node.left);
queue.offer(node.right);
} else {
values.add("null");
}
}
// Trim trailing nulls
while (!values.isEmpty() && "null".equals(values.get(values.size() - 1))) {
values.remove(values.size() - 1);
}
System.out.println(values);
}
// Main method to demonstrate tree inversion
public static void main(String[] args) {
// Create a sample tree:
// 4
// / \
// 2 7
// / \ / \
// 1 3 6 9
Solution.TreeNode root = new Solution.TreeNode(4);
root.left = new Solution.TreeNode(2);
root.right = new Solution.TreeNode(7);
root.left.left = new Solution.TreeNode(1);
root.left.right = new Solution.TreeNode(3);
root.right.left = new Solution.TreeNode(6);
root.right.right = new Solution.TreeNode(9);
Solution solution = new Solution();
System.out.println("Original tree (level order):");
printTree(root);
Solution.TreeNode inverted = solution.invertTree(root);
System.out.println("Inverted tree (level order):");
printTree(inverted);
}
}
Walkthrough
The operation is local at every node: mirror the left and right subtrees, then swap them. Each node is visited once. An induction on subtree size proves the returned tree is exactly the mirror image. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Null root; single node; skewed tree and stack depth.
Watch outs: Swapping after overwriting a child without a temporary reference; returning the wrong root.
D13. Best Time to Buy and Sell Stock
O(n) time and O(1) space.Track the minimum price so far and the best profit obtainable today.Animated
Given daily prices, maximize profit from at most one buy followed by one later sell.
Approach
Track the minimum price so far and the best profit obtainable today. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Initialize minPrice = ∞, best = 0.
For each price: update minPrice = min(minPrice, price).
Update best = max(best, price - minPrice).
Return best (0 if no profitable trade).
Code
import java.util.*;
/**
* Main entry point for the Best Time to Buy and Sell Stock demo.
* Contains the Solution class and a main method that runs several test cases.
*/
public class Main {
/**
* Solution for LeetCode 121: Best Time to Buy and Sell Stock.
* Tracks the minimum price seen so far and the maximum profit achievable.
*/
static class Solution {
/**
* Calculates the maximum profit from a single buy-sell transaction.
* @param prices array of daily stock prices
* @return maximum profit (0 if no profit is possible)
*/
public int maxProfit(int[] prices) {
int minPrice = Integer.MAX_VALUE, maxProfit = 0; // initialize min to largest int, profit to 0
for (int p : prices) { // iterate through each price
minPrice = Math.min(minPrice, p); // update minimum price seen so far
maxProfit = Math.max(maxProfit, p - minPrice); // update max profit if selling today yields more
}
return maxProfit; // return the best profit found
}
}
/**
* Runs the Solution on a few sample inputs and prints the results.
* @param args command-line arguments (unused)
*/
public static void main(String[] args) {
Solution solver = new Solution();
// Test case 1: typical profit scenario
int[] prices1 = {7, 1, 5, 3, 6, 4};
System.out.println("Test 1: " + solver.maxProfit(prices1)); // expected 5
// Test case 2: decreasing prices, no profit
int[] prices2 = {7, 6, 4, 3, 1};
System.out.println("Test 2: " + solver.maxProfit(prices2)); // expected 0
// Test case 3: profit after a dip
int[] prices3 = {2, 4, 1};
System.out.println("Test 3: " + solver.maxProfit(prices3)); // expected 2
// Test case 4: empty array
int[] prices4 = {};
System.out.println("Test 4: " + solver.maxProfit(prices4)); // expected 0
// Test case 5: single price
int[] prices5 = {1};
System.out.println("Test 5: " + solver.maxProfit(prices5)); // expected 0
}
}
Walkthrough
I scan once, maintaining the cheapest price seen before or at the current day. Selling today against that minimum gives the best profit ending today. Taking the maximum over all days gives the global optimum, with zero representing no transaction. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty/one price; decreasing prices; equal prices; large values.
Watch outs: Allowing sell before buy; returning a negative loss; updating minimum after computing with the wrong order.
Ready
D14. Coin Change
O(amount * numberOfCoins) time and O(amount) space.Use bottom-up DP where each amount takes the best predecessor plus one coin.Animated
Given coin denominations and an amount, return the minimum number of coins needed, or -1 if impossible.
Approach
Use bottom-up DP where each amount takes the best predecessor plus one coin. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Let dp[0]=0 and dp[1..amount]=∞ (or amount+1 as sentinel).
For each amount a from 1..amount, for each coin c ≤ a: dp[a] = min(dp[a], dp[a-c] + 1).
If dp[amount] is still sentinel, return -1 (impossible).
Otherwise return dp[amount].
Code
import java.util.*;
/**
* Main entry point for the coin change demonstration.
* Contains the Solution class as a static nested class.
*/
public class Main {
/**
* Solution class implementing the bottom-up DP coin change algorithm.
* Computes the minimum number of coins needed to make up a given amount.
*/
static class Solution {
/**
* Returns the minimum number of coins needed to make up 'amount'.
* @param coins array of coin denominations
* @param amount target amount
* @return minimum coins needed, or -1 if impossible
*/
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1]; // DP table where dp[i] = min coins for amount i
Arrays.fill(dp, amount + 1); // initialize with sentinel (amount+1 = "infinity")
dp[0] = 0; // base case: 0 coins needed for amount 0
for (int i = 1; i <= amount; i++) // compute dp for each amount 1..amount
for (int c : coins) // try each coin
if (c <= i) dp[i] = Math.min(dp[i], dp[i - c] + 1); // use coin c if it fits
return dp[amount] > amount ? -1 : dp[amount]; // if still sentinel, impossible; else answer
}
}
/**
* Main method: runs a few test cases and prints results.
*/
public static void main(String[] args) {
Solution sol = new Solution();
// Test 1: standard case
int[] coins1 = {1, 2, 5};
int amount1 = 11;
System.out.println("Test 1: coins=" + Arrays.toString(coins1) + ", amount=" + amount1);
System.out.println("Minimum coins: " + sol.coinChange(coins1, amount1)); // expected 3 (5+5+1)
// Test 2: impossible case
int[] coins2 = {2};
int amount2 = 3;
System.out.println("\nTest 2: coins=" + Arrays.toString(coins2) + ", amount=" + amount2);
System.out.println("Minimum coins: " + sol.coinChange(coins2, amount2)); // expected -1
// Test 3: greedy (4+1+1) would use 3 coins; the DP finds 3+3
int[] coins3 = {1, 3, 4};
int amount3 = 6;
System.out.println("\nTest 3: coins=" + Arrays.toString(coins3) + ", amount=" + amount3);
System.out.println("Minimum coins: " + sol.coinChange(coins3, amount3)); // expected 2 (3+3)
// Test 4: amount zero
int[] coins4 = {1, 2, 5};
int amount4 = 0;
System.out.println("\nTest 4: coins=" + Arrays.toString(coins4) + ", amount=" + amount4);
System.out.println("Minimum coins: " + sol.coinChange(coins4, amount4)); // expected 0
}
}
Walkthrough
I define dp[x] as the fewest coins for amount x, initialize all states unreachable except dp[0], and try every coin as the last coin. Since each transition comes from a smaller amount whose optimum is known, the final state is optimal or remains unreachable. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Amount zero; no coins; unreachable amount; duplicate or invalid denominations.
Watch outs: Using zero as an unreachable sentinel; integer overflow from MAX_VALUE + 1; confusing combinations with minimum count.
Ready
D15. Course Schedule
O(V + E) time and O(V + E) space.Run Kahn's topological sort using indegrees.Animated
Given prerequisite pairs, determine whether all courses can be completed.
Approach
Run Kahn's topological sort using indegrees. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Build adjacency list and indegree array from prerequisites.
Enqueue every course with indegree 0.
While queue nonempty: take a course, count it taken, decrement neighbors' indegrees; enqueue any that hit 0.
Return true iff you took all n courses (otherwise a cycle remains).
Code
import java.util.*;
/**
* Main class to demonstrate the Course Schedule solution (LeetCode 207).
* Uses Kahn's algorithm (BFS topological sort) to detect cycles in a directed graph.
*/
public class Main {
/**
* Static nested class containing the original solution.
* Kept exactly as provided in the snippet.
*/
static class Solution {
/**
* Determines if all courses can be finished given prerequisites.
* @param numCourses total number of courses labeled 0..numCourses-1
* @param prerequisites array of [course, pre] pairs meaning pre must be taken before course
* @return true if no cycle exists (all courses can be taken), false otherwise
*/
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<List<Integer>> graph = buildGraph(numCourses, prerequisites);
int[] inDegree = buildInDegree(numCourses, prerequisites);
return topologicalSort(graph, inDegree, numCourses);
}
/**
* Builds adjacency list representation of the directed graph.
* graph[u] contains all v such that u -> v (u is prerequisite of v).
*/
private List<List<Integer>> buildGraph(int n, int[][] prerequisites) {
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
for (int[] p : prerequisites) graph.get(p[1]).add(p[0]);
return graph;
}
/**
* Computes indegree (number of incoming edges) for each node.
*/
private int[] buildInDegree(int n, int[][] prerequisites) {
int[] inDegree = new int[n];
for (int[] p : prerequisites) inDegree[p[0]]++;
return inDegree;
}
/**
* Performs Kahn's topological sort.
* Returns true iff all n nodes are processed (no cycle).
*/
private boolean topologicalSort(List<List<Integer>> graph, int[] inDegree, int n) {
Queue<Integer> q = new ArrayDeque<>();
for (int i = 0; i < n; i++)
if (inDegree[i] == 0) q.offer(i);
int processed = 0;
while (!q.isEmpty()) {
int course = q.poll();
processed++;
for (int next : graph.get(course)) {
if (--inDegree[next] == 0) q.offer(next);
}
}
return processed == n;
}
}
/**
* Main method to run sample test cases and print results.
*/
public static void main(String[] args) {
Solution sol = new Solution();
// Test case 1: 2 courses, 1 prerequisite -> possible
int n1 = 2;
int[][] pre1 = {{1, 0}};
System.out.println("Test 1 (2 courses, [[1,0]]): " + sol.canFinish(n1, pre1)); // expected true
// Test case 2: 2 courses, mutual prerequisites -> cycle -> impossible
int n2 = 2;
int[][] pre2 = {{1, 0}, {0, 1}};
System.out.println("Test 2 (2 courses, [[1,0],[0,1]]): " + sol.canFinish(n2, pre2)); // expected false
// Test case 3: 4 courses, more complex prerequisites
int n3 = 4;
int[][] pre3 = {{1, 0}, {2, 0}, {3, 1}, {3, 2}};
System.out.println("Test 3 (4 courses, [[1,0],[2,0],[3,1],[3,2]]): " + sol.canFinish(n3, pre3)); // expected true
// Test case 4: 3 courses with cycle
int n4 = 3;
int[][] pre4 = {{0, 1}, {1, 2}, {2, 0}};
System.out.println("Test 4 (3 courses, [[0,1],[1,2],[2,0]]): " + sol.canFinish(n4, pre4)); // expected false
// Test case 5: single course, no prerequisites
int n5 = 1;
int[][] pre5 = {};
System.out.println("Test 5 (1 course, []): " + sol.canFinish(n5, pre5)); // expected true
}
}
Walkthrough
I build edges from prerequisite to dependent course and count indegrees. I repeatedly take zero-indegree courses and remove their outgoing edges. If every course is processed, a topological order exists; otherwise a cycle blocks the remaining courses. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: No prerequisites; self-loop; disconnected components; duplicate edges if allowed.
Watch outs: Reversing edge direction; not counting processed nodes; decrementing the wrong indegree.
Ready
D16. 3Sum
O(n^2) time plus output and O(log n) sort stack space (or implementation-dependent).Sort, fix one value, and use two pointers while skipping duplicates.Animated
Return all unique triplets whose values sum to zero.
Approach
Sort, fix one value, and use two pointers while skipping duplicates. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Sort nums ascending.
For each index i (skip duplicate nums[i]): set left=i+1, right=n-1.
While left < right: move pointers by sign of sum; on zero, record the triplet and skip duplicate left/right values.
Return the list of unique triplets.
Code
import java.util.*;
/**
* Main class to demonstrate the threeSum solution.
* Contains a static nested Solution class with the threeSum method.
*/
public class Main {
/**
* Solution class implementing the 3Sum algorithm.
* Finds all unique triplets in the array that sum to zero.
*/
static class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums); // Sort the array to enable two-pointer technique
List<List<Integer>> answer = new ArrayList<>(); // Result list
for (int i = 0; i < nums.length - 2; i++) { // Fix the first element
if (i > 0 && nums[i] == nums[i - 1]) continue; // Skip duplicate first elements
int left = i + 1, right = nums.length - 1; // Two pointers
while (left < right) { // Move pointers towards each other
long sum = (long) nums[i] + nums[left] + nums[right]; // Use long to avoid overflow
if (sum < 0) left++; // Sum too small, increase left
else if (sum > 0) right--; // Sum too large, decrease right
else { // Found a triplet summing to zero
answer.add(List.of(nums[i], nums[left], nums[right])); // Add immutable list
int low = nums[left], high = nums[right]; // Remember values to skip duplicates
while (left < right && nums[left] == low) left++; // Skip duplicate left values
while (left < right && nums[right] == high) right--; // Skip duplicate right values
}
}
}
return answer;
}
}
public static void main(String[] args) {
Solution solver = new Solution();
// Test case 1: typical example
int[] nums1 = {-1, 0, 1, 2, -1, -4};
List<List<Integer>> result1 = solver.threeSum(nums1);
System.out.println("Test 1: " + result1);
// Test case 2: all zeros
int[] nums2 = {0, 0, 0, 0};
List<List<Integer>> result2 = solver.threeSum(nums2);
System.out.println("Test 2: " + result2);
// Test case 3: no triplet
int[] nums3 = {1, 2, 3, 4};
List<List<Integer>> result3 = solver.threeSum(nums3);
System.out.println("Test 3: " + result3);
// Test case 4: empty array
int[] nums4 = {};
List<List<Integer>> result4 = solver.threeSum(nums4);
System.out.println("Test 4: " + result4);
}
}
Walkthrough
After sorting, I fix each distinct first value. The remaining two-sum is solved with left and right pointers: low sums move left, high sums move right. On a match I record once and skip equal values. This reduces cubic enumeration to quadratic time. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Fewer than three values; all zeros; duplicates; integer overflow.
Watch outs: Skipping duplicates at the wrong time; moving only one pointer after a match; forgetting sort mutates input.
Ready
D17. Longest Substring Without Repeating Characters
O(n) time and O(character-set size) space.Use a sliding window and last-seen indices to jump its left boundary.Animated
Return the length of the longest substring containing no repeated characters.
Approach
Use a sliding window and last-seen indices to jump its left boundary. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Keep a map of character → last seen index; left=0, answer=0.
Expand right across the string; if the char was seen at ≥ left, set left = previous+1.
Update the last-seen index for the current char.
Track answer = max(answer, right-left+1) and return it.
Code
import java.util.*;
/**
* Main class containing the Solution for finding the length of the longest substring without repeating characters.
* This is a complete, runnable program that demonstrates the sliding window algorithm.
*/
public class Main {
/**
* Solution class implementing the longest substring without repeating characters algorithm.
* Uses a HashMap to track the last seen index of each character and a sliding window approach.
*/
static class Solution {
public int lengthOfLongestSubstring(String text) {
Map<Character, Integer> last = new HashMap<>();
int left = 0, answer = 0;
for (int right = 0; right < text.length(); right++) {
char current = text.charAt(right);
Integer previous = last.put(current, right);
if (previous != null && previous >= left) left = previous + 1;
answer = Math.max(answer, right - left + 1);
}
return answer;
}
}
/**
* Main method to demonstrate the Solution with various test cases.
* Creates a Solution instance and tests it with different input strings.
*/
public static void main(String[] args) {
Solution solution = new Solution();
// Test case 1: Basic case with repeating characters
String test1 = "abcabcbb";
int result1 = solution.lengthOfLongestSubstring(test1);
System.out.println("Input: \"" + test1 + "\" -> Longest substring length: " + result1); // Expected: 3 ("abc")
// Test case 2: repeats in the middle (window restarts after the second 'w')
String test2 = "pwwkew";
int result2 = solution.lengthOfLongestSubstring(test2);
System.out.println("Input: \"" + test2 + "\" -> Longest substring length: " + result2); // Expected: 3 ("wke")
// Test case 3: Empty string
String test3 = "";
int result3 = solution.lengthOfLongestSubstring(test3);
System.out.println("Input: \"" + test3 + "\" -> Longest substring length: " + result3); // Expected: 0
// Test case 4: Single character
String test4 = "a";
int result4 = solution.lengthOfLongestSubstring(test4);
System.out.println("Input: \"" + test4 + "\" -> Longest substring length: " + result4); // Expected: 1
// Test case 5: All same characters
String test5 = "aaaaa";
int result5 = solution.lengthOfLongestSubstring(test5);
System.out.println("Input: \"" + test5 + "\" -> Longest substring length: " + result5); // Expected: 1
// Test case 6: No repeating characters
String test6 = "abcdef";
int result6 = solution.lengthOfLongestSubstring(test6);
System.out.println("Input: \"" + test6 + "\" -> Longest substring length: " + result6); // Expected: 6
}
}
Walkthrough
I maintain a duplicate-free window. For each right character, if its last occurrence is inside the window, I jump left just after that occurrence, never backward. Each character is processed once, so the maximum window length is found in linear time. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty string; one character; Unicode beyond ASCII; repeated character before current window.
Watch outs: Moving left backward; confusing substring with subsequence; assuming ASCII without stating it.
Ready
D18. Merge Intervals
O(n log n) time and O(n) output space.Sort by start, then extend the last merged interval or append a new one.Animated
Merge all overlapping intervals and return the disjoint union.
Approach
Sort by start, then extend the last merged interval or append a new one. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Sort intervals by start time.
Initialize merged empty; for each interval: if it starts after the last merged end, append it.
Otherwise extend the last merged end to max(end, interval.end).
Return merged as an array.
Code
import java.util.*;
// Main class to demonstrate the interval merging solution
public class Main {
/**
* Solution class that provides the merge method for combining overlapping intervals.
* The snippet's code is unchanged; it is only nested so everything fits in one file.
*/
static class Solution {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); // Sort intervals by start time
List<int[]> merged = new ArrayList<>(); // List to store merged intervals
for (int[] iv : intervals) { // Iterate through each interval
if (merged.isEmpty() || merged.get(merged.size()-1)[1] < iv[0]) // If no overlap with last merged
merged.add(iv); // Add as new interval
else
merged.get(merged.size()-1)[1] = Math.max(merged.get(merged.size()-1)[1], iv[1]); // Extend last merged interval
}
return merged.toArray(new int[0][]); // Convert list to array and return
}
}
// Helper method to print 2D array in readable format
static void printIntervals(int[][] intervals) {
System.out.print("[");
for (int i = 0; i < intervals.length; i++) {
System.out.print("[" + intervals[i][0] + "," + intervals[i][1] + "]");
if (i < intervals.length - 1) System.out.print(",");
}
System.out.println("]");
}
public static void main(String[] args) {
Solution sol = new Solution();
// Test case 1: Basic overlapping intervals
int[][] intervals1 = {{1,3},{2,6},{8,10},{15,18}};
System.out.print("Test 1 - Input: ");
printIntervals(intervals1);
int[][] result1 = sol.merge(intervals1);
System.out.print("Output: ");
printIntervals(result1);
// Test case 2: Intervals that touch and merge
int[][] intervals2 = {{1,4},{4,5}};
System.out.print("Test 2 - Input: ");
printIntervals(intervals2);
int[][] result2 = sol.merge(intervals2);
System.out.print("Output: ");
printIntervals(result2);
// Test case 3: Non-overlapping intervals
int[][] intervals3 = {{1,2},{3,4},{5,6}};
System.out.print("Test 3 - Input: ");
printIntervals(intervals3);
int[][] result3 = sol.merge(intervals3);
System.out.print("Output: ");
printIntervals(result3);
// Test case 4: Single interval
int[][] intervals4 = {{1,5}};
System.out.print("Test 4 - Input: ");
printIntervals(intervals4);
int[][] result4 = sol.merge(intervals4);
System.out.print("Output: ");
printIntervals(result4);
// Test case 5: Empty array (edge case)
int[][] intervals5 = {};
System.out.print("Test 5 - Input: ");
printIntervals(intervals5);
int[][] result5 = sol.merge(intervals5);
System.out.print("Output: ");
printIntervals(result5);
}
}
Walkthrough
Sorting makes all potential overlaps adjacent. I keep a merged result; if the next start is within the last merged end, I extend that end, otherwise I start a new component. The invariant is that the result is the exact disjoint union of the processed prefix. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Watch outs: Not sorting; comparing with the original prior interval instead of the merged end; mishandling endpoint semantics.
Ready
D19. K-Step Stair Ways
O(n) time and O(n) space as shown; O(k) space with a circular buffer.Use DP with a sliding sum of the previous k states.
Canonical interpretation: count ordered ways to reach stair n when each move may climb from 1 through k stairs.
Approach
Use DP with a sliding sum of the previous k states. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Let ways[0]=1; maintain a sliding window sum of the last up to k ways.
For stair s=1..n: ways[s] = window.
Add ways[s] into the window; if s-k ≥ 0, subtract ways[s-k].
Return ways[n].
Code
public class Main {
/**
* Solution class that computes the number of ways to climb n stairs
* when each move may climb from 1 through k stairs.
*/
static class Solution {
/**
* Computes the number of distinct ways to reach step n using steps of size 1..k.
* Uses dynamic programming with a sliding window sum of the last k states.
* @param n the target stair (non-negative)
* @param k the maximum step size (positive)
* @return number of ways to reach stair n
* @throws IllegalArgumentException if n < 0 or k <= 0
*/
public long countWays(int n, int k) {
if (n < 0 || k <= 0) throw new IllegalArgumentException(); // validate input
long[] ways = new long[n + 1]; // DP array where ways[i] = ways to reach stair i
ways[0] = 1; // base case: one way to stay at ground (stair 0)
long window = 1; // sliding sum of the last up to k ways, initially ways[0]
for (int stair = 1; stair <= n; stair++) { // iterate through each stair
ways[stair] = window; // ways to reach this stair equals sum of previous k ways
window += ways[stair]; // add current ways to the window
if (stair - k >= 0) window -= ways[stair - k]; // remove ways that are now out of the window
}
return ways[n]; // return ways to reach the target stair
}
}
public static void main(String[] args) {
Solution sol = new Solution(); // create an instance of Solution
// Example 1: climb 4 stairs with steps up to 2 -> 5 ways (1+1+1+1, 1+1+2, 1+2+1, 2+1+1, 2+2)
System.out.println("Ways(4,2) = " + sol.countWays(4, 2));
// Example 2: climb 5 stairs with steps up to 3
System.out.println("Ways(5,3) = " + sol.countWays(5, 3));
// Example 3: zero stairs -> 1 way (do nothing)
System.out.println("Ways(0,5) = " + sol.countWays(0, 5));
// Example 4: only step size 1 -> exactly 1 way
System.out.println("Ways(3,1) = " + sol.countWays(3, 1));
}
}
Walkthrough
I define dp[i] as ordered ways to reach stair i. The last jump can be any size from one to k, so dp[i] is the sum of the prior k states. Maintaining that sum as a rolling window improves the straightforward O(nk) recurrence to O(n). I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Watch outs: Treating order as irrelevant; off-by-one window removal; failing to define the empty route.
D20. Spiral Matrix
O(rows * cols) time and O(1) auxiliary space excluding output.Shrink top, bottom, left, and right boundaries after traversing each side.Animated
Return all elements of a matrix in clockwise spiral order.
Approach
Shrink top, bottom, left, and right boundaries after traversing each side. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Set bounds top,bottom,left,right to the matrix edges.
While bounds are valid: traverse top row L→R then top++; right column T→B then right--.
If still valid: bottom row R→L then bottom--; left column B→T then left++.
Append visited cells to the result and return it.
Code
import java.util.*;
public class Main {
// Solution class that provides spiral order traversal of a 2D matrix
static class Solution {
// Returns elements of matrix in spiral order
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> result = new ArrayList<>(); // result list
if (matrix.length == 0) return result; // handle empty matrix
int top = 0, bottom = matrix.length - 1; // row boundaries
int left = 0, right = matrix[0].length - 1; // column boundaries
while (top <= bottom && left <= right) { // while bounds valid
for (int c = left; c <= right; c++) result.add(matrix[top][c]); // top row left->right
top++; // shrink top boundary
for (int r = top; r <= bottom; r++) result.add(matrix[r][right]); // right column top->bottom
right--; // shrink right boundary
if (top <= bottom) { // if rows remain
for (int c = right; c >= left; c--) result.add(matrix[bottom][c]); // bottom row right->left
bottom--; // shrink bottom boundary
}
if (left <= right) { // if columns remain
for (int r = bottom; r >= top; r--) result.add(matrix[r][left]); // left column bottom->top
left++; // shrink left boundary
}
}
return result;
}
}
// Main method to demonstrate spiralOrder with various test cases
public static void main(String[] args) {
Solution sol = new Solution();
// Test case 1: 3x3 matrix
int[][] matrix1 = {{1,2,3},{4,5,6},{7,8,9}};
System.out.println("Spiral order of 3x3: " + sol.spiralOrder(matrix1));
// Test case 2: 3x4 matrix
int[][] matrix2 = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
System.out.println("Spiral order of 3x4: " + sol.spiralOrder(matrix2));
// Test case 3: empty matrix
int[][] matrix3 = {{}};
System.out.println("Spiral order of empty: " + sol.spiralOrder(matrix3));
// Test case 4: single row
int[][] matrix4 = {{1,2,3,4}};
System.out.println("Spiral order of single row: " + sol.spiralOrder(matrix4));
// Test case 5: single column
int[][] matrix5 = {{1},{2},{3}};
System.out.println("Spiral order of single column: " + sol.spiralOrder(matrix5));
}
}
Walkthrough
I maintain the rectangle that has not yet been emitted. I traverse its top, right, bottom, and left edges, shrinking each corresponding boundary. Conditional checks prevent duplicate traversal when only one row or column remains. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty matrix; one row/column; rectangular matrix; final center row/column.
O(n) time and O(1) space.Use a dummy head, locate each kth node, then reverse that bounded group in place.
Representative hard linked-list manipulation: reverse nodes in groups of exactly k; leave a final short group unchanged.
Approach
Use a dummy head, locate each kth node, then reverse that bounded group in place. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Dummy head before the list; groupPrev points just before the next group.
Advance k steps to find the kth node; if fewer than k remain, stop.
Reverse the k nodes in place so the kth becomes the new group head; reconnect groupPrev.
Advance groupPrev to the old start (now the group tail) and repeat; return dummy.next.
Code
import java.util.*;
/**
* Main entry point demonstrating the reverseKGroup algorithm.
* Reverses nodes of a linked list in groups of size k.
*/
public class Main {
/**
* Solution class containing the reverseKGroup method.
* Reverses every k nodes in a singly linked list.
*/
static class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if (k <= 0) throw new IllegalArgumentException("k must be positive");
ListNode dummy = new ListNode(0, head);
ListNode groupPrev = dummy;
while (true) {
ListNode kth = groupPrev;
for (int i = 0; i < k && kth != null; i++) kth = kth.next;
if (kth == null) break;
ListNode groupNext = kth.next, prev = groupNext, cur = groupPrev.next;
while (cur != groupNext) {
ListNode next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
ListNode oldStart = groupPrev.next;
groupPrev.next = kth;
groupPrev = oldStart;
}
return dummy.next;
}
}
/**
* ListNode class representing a node in a singly linked list.
* Contains an integer value and a reference to the next node.
*/
static class ListNode {
int val; ListNode next; // fields: value and next pointer
ListNode(int val) { this.val = val; } // constructor with value only
ListNode(int val, ListNode next) { this.val = val; this.next = next; } // constructor with value and next
}
/**
* Builds a linked list from an array of integers.
* @param values array of ints
* @return head of the linked list
*/
static ListNode buildList(int[] values) {
ListNode dummy = new ListNode(0);
ListNode cur = dummy;
for (int v : values) {
cur.next = new ListNode(v);
cur = cur.next;
}
return dummy.next;
}
/**
* Prints a linked list in the format val1 -> val2 -> ... -> null
* @param head head of the list
*/
static void printList(ListNode head) {
ListNode cur = head;
while (cur != null) {
System.out.print(cur.val);
if (cur.next != null) System.out.print(" -> ");
cur = cur.next;
}
System.out.println(" -> null");
}
/**
* Main method: creates a sample list, reverses in groups of k, and prints results.
*/
public static void main(String[] args) {
int[] data = {1, 2, 3, 4, 5};
int k = 2;
ListNode head = buildList(data);
System.out.println("Original list:");
printList(head);
Solution sol = new Solution();
ListNode newHead = sol.reverseKGroup(head, k);
System.out.println("After reverseKGroup with k=" + k + ":");
printList(newHead);
// Additional test: k=3
ListNode head2 = buildList(new int[]{1,2,3,4,5,6,7});
System.out.println("\nOriginal list 2:");
printList(head2);
ListNode newHead2 = sol.reverseKGroup(head2, 3);
System.out.println("After reverseKGroup with k=3:");
printList(newHead2);
}
}
Walkthrough
I use a dummy node and first verify a complete group exists by finding its kth node. I save the node after the group, reverse pointers until that boundary, reconnect the old group head as the new tail, and continue. Every node is touched a constant number of times. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty list; k=1; fewer than k nodes; exact multiple; invalid k contract.
Watch outs: Reversing the short tail; losing groupNext; advancing groupPrev to the wrong node.
D22. Number of Provinces via DFS/BFS
O(n^2) time and O(n) space.Launch DFS from each unvisited vertex; BFS is a direct queue-based follow-up.
Representative DFS/BFS item: given an adjacency matrix of an undirected graph, count connected components.
Approach
Launch DFS from each unvisited vertex; BFS is a direct queue-based follow-up. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Treat the adjacency matrix as an undirected graph; seen[n]=false, provinces=0.
For each city not yet seen: increment provinces and DFS/BFS-mark its connected component.
DFS visits every neighbor j with graph[i][j]==1 that is unseen.
Return provinces.
Code
// Imports utility classes like ArrayList, HashMap, etc. (though not all used here).
import java.util.*;
// Main class to run the solution
// Main entry point class that contains the Solution and runs test cases.
public class Main {
// Solution class from the snippet - finds number of provinces in adjacency matrix
// Static nested class Solution encapsulates the algorithm to count provinces (connected components) in an adjacency matrix.
static class Solution {
// Public method findCircleNum takes adjacency matrix 'connected' and returns number of provinces.
public int findCircleNum(int[][] connected) {
// Initialize n as number of cities, provinces counter to zero.
int n = connected.length, provinces = 0;
// Boolean array 'seen' tracks visited cities to avoid revisiting.
boolean[] seen = new boolean[n];
// Loop through each city to start DFS if not yet visited.
for (int city = 0; city < n; city++) {
// If city not seen, it's a new province.
if (!seen[city]) {
// Increment province count.
provinces++;
// Call DFS to mark all cities in this province as seen.
dfs(city, connected, seen);
}
}
// Return total provinces found.
return provinces;
}
// Private helper DFS marks city as visited and recursively visits connected neighbors.
private void dfs(int city, int[][] graph, boolean[] seen) {
// Mark current city as visited.
seen[city] = true;
// Iterate over all potential neighbors.
for (int next = 0; next < graph.length; next++)
// If neighbor is connected (value 1) and not visited, recursively DFS into it.
if (graph[city][next] == 1 && !seen[next]) dfs(next, graph, seen);
}
}
// Main method runs the test cases.
public static void main(String[] args) {
// Create Solution instance.
Solution sol = new Solution();
// Test case 1: 3 cities, 2 provinces (0-1 connected, 2 isolated)
// Define adjacency matrix for test case 1.
int[][] graph1 = {
{1, 1, 0},
{1, 1, 0},
{0, 0, 1}
};
// Print result of findCircleNum for graph1, expected 2.
System.out.println("Test 1 - Expected 2: " + sol.findCircleNum(graph1));
// Test case 2: 3 cities, 1 province (all connected)
// Define adjacency matrix for test case 2 (all ones).
int[][] graph2 = {
{1, 1, 1},
{1, 1, 1},
{1, 1, 1}
};
// Print result for graph2, expected 1.
System.out.println("Test 2 - Expected 1: " + sol.findCircleNum(graph2));
// Test case 3: 4 cities, 3 provinces (0-1 connected, 2 isolated, 3 isolated)
// Define adjacency matrix for test case 3.
int[][] graph3 = {
{1, 1, 0, 0},
{1, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1}
};
// Print result for graph3, expected 3.
System.out.println("Test 3 - Expected 3: " + sol.findCircleNum(graph3));
// Test case 4: Single city
// Define adjacency matrix for single city.
int[][] graph4 = {{1}};
// Print result for graph4, expected 1.
System.out.println("Test 4 - Expected 1: " + sol.findCircleNum(graph4));
}
}
Walkthrough
I scan every vertex and launch DFS only if it is unvisited. DFS marks every vertex reachable in that connected component. Therefore each component increments the answer exactly once. With an adjacency matrix we inspect up to n neighbors per vertex, giving quadratic time. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty graph; isolated vertices; one component; matrix symmetry/diagonal assumptions.
Watch outs: Counting edges; marking too late in BFS; overlooking disconnected vertices.
D23. Sliding Window Maximum
O(n) time and O(k) space.Maintain a deque of candidate indices in decreasing value order.Animated
Canonical sliding-window buffer problem: return the maximum in every contiguous window of size k.
Approach
Maintain a deque of candidate indices in decreasing value order. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Deque of candidate indices in decreasing nums order.
For each index i: drop indices outside the window [i-k+1, i] from the front.
Pop back while the back's value is ≤ nums[i]; push i.
Once i ≥ k-1, record nums[deque.front] as the window maximum.
Code
import java.util.*;
/**
* Main class containing the sliding window maximum solution and a demo.
*/
public class Main {
/**
* Solution class implementing the monotonic deque algorithm for
* finding maximums in all sliding windows of size k.
*/
static class Solution {
/**
* Returns an array of the maximum values in each sliding window of size k.
* Uses a deque to store candidate indices in decreasing order of their values.
* @param nums input array of integers
* @param k window size
* @return array of window maximums
*/
public int[] maxSlidingWindow(int[] nums, int k) {
Deque<Integer> dq = new ArrayDeque<>(); // indices
int[] result = new int[nums.length - k + 1];
for (int i = 0; i < nums.length; i++) {
while (!dq.isEmpty() && dq.peekFirst() < i - k + 1) dq.pollFirst();
while (!dq.isEmpty() && nums[dq.peekLast()] < nums[i]) dq.pollLast();
dq.offerLast(i);
if (i >= k - 1) result[i - k + 1] = nums[dq.peekFirst()];
}
return result;
}
}
/**
* Main method demonstrating the sliding window maximum algorithm with sample inputs.
* @param args command line arguments (unused)
*/
public static void main(String[] args) {
Solution sol = new Solution();
// Example 1: typical case
int[] nums1 = {1, 3, -1, -3, 5, 3, 6, 7};
int k1 = 3;
int[] result1 = sol.maxSlidingWindow(nums1, k1);
System.out.print("Example 1: nums=" + Arrays.toString(nums1) + ", k=" + k1 + " -> ");
System.out.println(Arrays.toString(result1)); // expected [3,3,5,5,6,7]
// Example 2: single element window
int[] nums2 = {1, -1};
int k2 = 1;
int[] result2 = sol.maxSlidingWindow(nums2, k2);
System.out.print("Example 2: nums=" + Arrays.toString(nums2) + ", k=" + k2 + " -> ");
System.out.println(Arrays.toString(result2)); // expected [1,-1]
// Example 3: decreasing array
int[] nums3 = {9, 8, 7, 6, 5};
int k3 = 2;
int[] result3 = sol.maxSlidingWindow(nums3, k3);
System.out.print("Example 3: nums=" + Arrays.toString(nums3) + ", k=" + k3 + " -> ");
System.out.println(Arrays.toString(result3)); // expected [9,8,7,6]
// Example 4: increasing array
int[] nums4 = {1, 2, 3, 4, 5};
int k4 = 3;
int[] result4 = sol.maxSlidingWindow(nums4, k4);
System.out.print("Example 4: nums=" + Arrays.toString(nums4) + ", k=" + k4 + " -> ");
System.out.println(Arrays.toString(result4)); // expected [3,4,5]
}
}
Walkthrough
The deque stores only useful candidate indices in decreasing value order. Before adding an element I remove expired indices and any smaller trailing values that can never win again. The front is then the current maximum. Each index enters and leaves once, so runtime is linear. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges:k=1; k=n; duplicates; invalid k; all decreasing values.
Watch outs: Storing stale indices; storing only values when expiry matters; using a heap without lazy deletion.
Ready
D24. Valid Parentheses
O(n) time and O(n) space.Push expected closing brackets and match each closing character against the stack top.Animated
Determine whether a string of ()[]{} brackets is correctly matched and nested.
Approach
Push expected closing brackets and match each closing character against the stack top. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Map each closing bracket to its opening partner; use a stack of opens.
On open '('/'['/'{', push it.
On close: if stack empty or top ≠ expected open, return false; else pop.
At end return whether the stack is empty.
Code
import java.util.*;
/**
* Main class to demonstrate the bracket validation solution.
* Contains the Solution class as a static nested class.
*/
public class Main {
/**
* Solution class that checks if a string of brackets is valid.
* Uses a stack to track opening brackets and a map for closing-to-opening pairs.
*/
static class Solution {
/**
* Determines if the input string has valid bracket pairing.
* @param s string containing only '(', ')', '[', ']', '{', '}'
* @return true if all brackets are properly closed and nested
*/
public boolean isValid(String s) {
Deque<Character> stack = new ArrayDeque<>(); // Stack to hold opening brackets
Map<Character, Character> map = Map.of(')', '(', ']', '[', '}', '{'); // Closing -> Opening map
for (char c : s.toCharArray()) { // Iterate through each character
if (map.containsKey(c)) { // If it's a closing bracket
if (stack.isEmpty() || stack.pop() != map.get(c)) return false; // Mismatch or empty stack
} else if (c == '(' || c == '[' || c == '{') { // If it's an opening bracket
stack.push(c); // Push onto stack
} else {
return false; // Invalid character (not a bracket)
}
}
return stack.isEmpty(); // Valid only if all opens were matched
}
}
/**
* Main method to test the isValid function with various inputs.
* Prints each test case and its result.
*/
public static void main(String[] args) {
Solution solver = new Solution(); // Create solution instance
// Test cases covering valid, invalid, and edge cases
String[] testCases = {
"()", // Valid: simple pair
"()[]{}", // Valid: multiple types
"(]", // Invalid: mismatch
"([)]", // Invalid: wrong nesting
"{[]}", // Valid: nested correctly
"", // Valid: empty string
"(((", // Invalid: unclosed opens
")))", // Invalid: unmatched closes
"[" // Invalid: single open
};
for (String test : testCases) {
boolean result = solver.isValid(test);
System.out.println("Input: \"" + test + "\" -> Valid: " + result);
}
}
}
Walkthrough
I push the closing bracket expected for every opener. Every closer must equal the current expected top, which enforces both type and nesting. Any mismatch fails immediately, and the string is valid only if no expectations remain. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Watch outs: Checking only counts; ignoring bracket type; popping an empty stack.
Ready
D25. Top K Frequent Elements
O(n) time and O(n) space.Count with a map, then place values into frequency buckets.
Given an integer array, return the k most frequent values in any order.
Approach
Count with a map, then place values into frequency buckets. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Count frequencies in a HashMap.
Place each value into buckets[freq] (lists indexed by count).
Scan buckets from high freq → low, collecting values until you have k.
Return the collected array.
Code
import java.util.*;
/**
* Main class to demonstrate the topKFrequent algorithm.
* Finds the k most frequent elements in an integer array using bucket sort on frequencies.
*/
public class Main {
/**
* Solution class containing the topKFrequent method.
* Uses a HashMap to count frequencies, then buckets indexed by frequency to collect values.
*/
static class Solution {
/**
* Returns the k most frequent values in any order.
* @param nums input array of integers
* @param k number of top frequent elements to return
* @return array of k most frequent integers
* @throws IllegalArgumentException if k is invalid
*/
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> freq = new HashMap<>();
for (int x : nums) freq.merge(x, 1, Integer::sum);
if (k < 1 || k > freq.size()) throw new IllegalArgumentException("invalid k");
@SuppressWarnings("unchecked")
List<Integer>[] buckets = new List[nums.length + 1];
for (var e : freq.entrySet()) {
int f = e.getValue();
if (buckets[f] == null) buckets[f] = new ArrayList<>();
buckets[f].add(e.getKey());
}
int[] answer = new int[k];
int p = 0;
for (int f = buckets.length - 1; f >= 0 && p < k; f--)
if (buckets[f] != null)
for (int x : buckets[f]) {
if (p == k) break;
answer[p++] = x;
}
return answer;
}
}
/**
* Main method: runs a demonstration of the topKFrequent algorithm.
* Creates sample input, calls the method, and prints the result.
*/
public static void main(String[] args) {
// Sample input array with repeated values
int[] nums = {1, 1, 1, 2, 2, 3, 4, 4, 4, 4, 5};
int k = 2; // We want the top 2 most frequent elements
Solution sol = new Solution();
int[] result = sol.topKFrequent(nums, k);
// Print the result array
System.out.print("Top " + k + " frequent elements: ");
for (int i = 0; i < result.length; i++) {
System.out.print(result[i]);
if (i < result.length - 1) System.out.print(", ");
}
System.out.println();
// Additional test case
int[] nums2 = {7, 7, 7, 7, 3, 3, 2, 2, 2, 1};
int k2 = 3;
int[] result2 = sol.topKFrequent(nums2, k2);
System.out.print("Top " + k2 + " frequent elements: ");
for (int i = 0; i < result2.length; i++) {
System.out.print(result2[i]);
if (i < result2.length - 1) System.out.print(", ");
}
System.out.println();
}
}
Walkthrough
I first count frequencies. Since no frequency exceeds n, I use an array of buckets where bucket f contains values appearing f times. Scanning from n downward and stopping after k values returns the most frequent elements in linear time. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges:k=1; all unique; k equals distinct count; tie order unspecified.
Watch outs: Sorting all entries unnecessarily; confusing k elements with frequency at least k; assuming deterministic tie order.
D26. Kth Largest Element in an Array
O(n) expected time, O(n^2) worst case, and O(1) auxiliary space.Use randomized quickselect for average linear time.
Return the kth largest element, counting duplicate occurrences.
Approach
Use randomized quickselect for average linear time. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Target index is n-k in ascending order (0-based).
Randomized quickselect: pick a pivot, partition so smaller values are left of the pivot index.
If pivot index == target, return that value; if less, search right; else search left.
Repeat until found (average O(n)).
Code
import java.util.concurrent.ThreadLocalRandom;
/**
* Main class containing the quickselect solution for finding the kth largest element.
* This demonstrates the randomized quickselect algorithm with average O(n) time complexity.
*/
public class Main {
/**
* Solution class implementing the quickselect algorithm to find the kth largest element.
* The algorithm partitions the array around a random pivot and recursively searches
* only the relevant partition, achieving average linear time.
*/
static class Solution {
/**
* Finds the kth largest element in the array using randomized quickselect.
* @param nums input array of integers
* @param k the order of the largest element to find (1 = largest, 2 = second largest, etc.)
* @return the kth largest element
* @throws IllegalArgumentException if k is out of bounds
*/
public int findKthLargest(int[] nums, int k) {
if (k < 1 || k > nums.length) throw new IllegalArgumentException("invalid k");
int target = nums.length - k, left = 0, right = nums.length - 1;
while (left <= right) {
int pivot = ThreadLocalRandom.current().nextInt(left, right + 1);
int value = nums[pivot];
swap(nums, pivot, right);
int store = left;
for (int i = left; i < right; i++) if (nums[i] < value) swap(nums, store++, i);
swap(nums, store, right);
if (store == target) return nums[store];
if (store < target) left = store + 1; else right = store - 1;
}
throw new AssertionError();
}
/**
* Swaps two elements in the array.
* @param a the array
* @param i first index
* @param j second index
*/
private void swap(int[] a, int i, int j) { int t = a[i]; a[i] = a[j]; a[j] = t; }
}
/**
* Main method to demonstrate the quickselect algorithm with various test cases.
* @param args command line arguments (not used)
*/
public static void main(String[] args) {
Solution sol = new Solution();
// Test case 1: Basic example
int[] nums1 = {3, 2, 1, 5, 6, 4};
int k1 = 2;
System.out.println("Test 1: nums = [3, 2, 1, 5, 6, 4], k = " + k1);
System.out.println(" 2nd largest element: " + sol.findKthLargest(nums1, k1)); // Expected: 5
// Test case 2: Duplicate elements
int[] nums2 = {3, 2, 3, 1, 2, 4, 5, 5, 6};
int k2 = 4;
System.out.println("Test 2: nums = [3, 2, 3, 1, 2, 4, 5, 5, 6], k = " + k2);
System.out.println(" 4th largest element: " + sol.findKthLargest(nums2, k2)); // Expected: 4
// Test case 3: Single element array
int[] nums3 = {42};
int k3 = 1;
System.out.println("Test 3: nums = [42], k = " + k3);
System.out.println(" 1st largest element: " + sol.findKthLargest(nums3, k3)); // Expected: 42
// Test case 4: Already sorted descending
int[] nums4 = {9, 8, 7, 6, 5, 4, 3, 2, 1};
int k4 = 3;
System.out.println("Test 4: nums = [9, 8, 7, 6, 5, 4, 3, 2, 1], k = " + k4);
System.out.println(" 3rd largest element: " + sol.findKthLargest(nums4, k4)); // Expected: 7
// Test case 5: Already sorted ascending
int[] nums5 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
int k5 = 5;
System.out.println("Test 5: nums = [1, 2, 3, 4, 5, 6, 7, 8, 9], k = " + k5);
System.out.println(" 5th largest element: " + sol.findKthLargest(nums5, k5)); // Expected: 5
// Test case 6: Negative numbers
int[] nums6 = {-1, -2, -3, -4, -5};
int k6 = 2;
System.out.println("Test 6: nums = [-1, -2, -3, -4, -5], k = " + k6);
System.out.println(" 2nd largest element: " + sol.findKthLargest(nums6, k6)); // Expected: -2
// Test case 7: k = 1 (largest element)
int[] nums7 = {10, 20, 30, 40, 50};
int k7 = 1;
System.out.println("Test 7: nums = [10, 20, 30, 40, 50], k = " + k7);
System.out.println(" Largest element: " + sol.findKthLargest(nums7, k7)); // Expected: 50
// Test case 8: k = length (smallest element)
int[] nums8 = {10, 20, 30, 40, 50};
int k8 = 5;
System.out.println("Test 8: nums = [10, 20, 30, 40, 50], k = " + k8);
System.out.println(" Smallest element: " + sol.findKthLargest(nums8, k8)); // Expected: 10
}
}
Walkthrough
Kth largest is index n-k in ascending order. Quickselect partitions around a randomized pivot, after which the pivot is final and only one side can contain the target. Repeating on that side gives expected linear time without sorting everything. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges:k=1; k=n; duplicates; already sorted data; invalid k.
Watch outs: Using kth distinct value; off-by-one converting largest to ascending index; deterministic bad pivots.
D27. Lowest Common Ancestor of a Binary Tree
O(n) time and O(h) recursion space.Use postorder recursion; propagate a found target upward and combine when targets split.
Given a binary tree and two existing nodes, return their lowest common ancestor.
Approach
Use postorder recursion; propagate a found target upward and combine when targets split. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
If root is null or equals p or q, return root.
Recurse on left and right subtrees.
If both sides return non-null, root is the LCA (targets split).
Otherwise return the non-null side (both targets under one child, or only one found).
Code
import java.util.*;
/**
* Main entry point demonstrating the Lowest Common Ancestor algorithm.
* The snippet provides Solution.lowestCommonAncestor and TreeNode classes.
*/
public class Main {
/**
* Solution class containing the LCA algorithm.
* Original snippet lines preserved exactly.
*/
static class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) return root;
return left != null ? left : right;
}
}
/**
* TreeNode class representing a binary tree node.
* Original snippet lines preserved exactly.
*/
static class TreeNode {
int val; TreeNode left, right;
TreeNode(int val) { this.val = val; }
}
/**
* Builds a sample binary tree:
* 3
* / \
* 5 1
* / \ / \
* 6 2 0 8
* / \
* 7 4
* Returns the root node (3).
*/
static TreeNode buildSampleTree() {
TreeNode n3 = new TreeNode(3);
TreeNode n5 = new TreeNode(5);
TreeNode n1 = new TreeNode(1);
TreeNode n6 = new TreeNode(6);
TreeNode n2 = new TreeNode(2);
TreeNode n0 = new TreeNode(0);
TreeNode n8 = new TreeNode(8);
TreeNode n7 = new TreeNode(7);
TreeNode n4 = new TreeNode(4);
n3.left = n5; n3.right = n1;
n5.left = n6; n5.right = n2;
n1.left = n0; n1.right = n8;
n2.left = n7; n2.right = n4;
return n3;
}
public static void main(String[] args) {
TreeNode root = buildSampleTree();
Solution sol = new Solution();
// LCA of nodes 5 and 1 should be 3 (root)
TreeNode lca1 = sol.lowestCommonAncestor(root, root.left, root.right);
System.out.println("LCA of 5 and 1: " + (lca1 != null ? lca1.val : "null"));
// LCA of nodes 5 and 4 should be 5 (5 is ancestor of 4)
TreeNode lca2 = sol.lowestCommonAncestor(root, root.left, root.left.right.right);
System.out.println("LCA of 5 and 4: " + (lca2 != null ? lca2.val : "null"));
// LCA of nodes 7 and 4 should be 2
TreeNode lca3 = sol.lowestCommonAncestor(root, root.left.right.left, root.left.right.right);
System.out.println("LCA of 7 and 4: " + (lca3 != null ? lca3.val : "null"));
// LCA of nodes 6 and 0 should be 3
TreeNode lca4 = sol.lowestCommonAncestor(root, root.left.left, root.right.left);
System.out.println("LCA of 6 and 0: " + (lca4 != null ? lca4.val : "null"));
}
}
Walkthrough
Postorder lets each subtree report whether it contains a target or an already-found LCA. If both sides report non-null, the current node is the first point where paths meet. If only one side reports, I propagate it; matching the current node also propagates it. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: One node is ancestor of the other; root is a target; skewed tree; nodes absent if not guaranteed.
Watch outs: Comparing values when nodes are identified by reference; returning before exploring required branches.
D28. Serialize and Deserialize Binary Tree
O(n) time and O(n) serialized/recursion space.Use preorder traversal with an explicit null marker.
Convert a binary tree to a string and reconstruct an identical tree.
Approach
Use preorder traversal with an explicit null marker. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Serialize: preorder DFS; write val, for nodes and #, for nulls.
Deserialize: split tokens; consume left-to-right.
On #, return null; else create node, then recursively build left then right.
Round-trip preserves structure because null markers encode missing children.
Code
// Imports Java utility classes for collections and arrays.
import java.util.*;
// Main class containing the program entry point and helper classes.
public class Main {
// Codec: serializes and deserializes a binary tree using preorder traversal with '#' for nulls
// Codec class that handles serialization and deserialization of a binary tree using preorder traversal with '#' as null marker.
static class Codec {
// Serializes a binary tree root into a comma-separated string.
public String serialize(TreeNode root) {
// Creates a StringBuilder to efficiently build the serialized string.
StringBuilder out = new StringBuilder();
// Invokes the recursive write helper to populate the StringBuilder.
write(root, out);
// Converts the StringBuilder to a string and returns it.
return out.toString();
}
// Recursive helper that writes node values or '#' for null into the StringBuilder.
private void write(TreeNode node, StringBuilder out) {
// If node is null, appends '#,' and returns.
if (node == null) { out.append("#,"); return; }
// Appends the node's value followed by a comma.
out.append(node.val).append(',');
// Recursively writes left and right subtrees.
write(node.left, out); write(node.right, out);
}
// Deserializes a comma-separated string back into a binary tree.
public TreeNode deserialize(String data) {
// Splits the input string by commas into a deque of tokens for sequential processing.
Deque<String> tokens = new ArrayDeque<>(Arrays.asList(data.split(",")));
// Calls the recursive read helper to reconstruct the tree.
return read(tokens);
}
// Recursive helper that reads tokens and builds tree nodes.
private TreeNode read(Deque<String> tokens) {
// Removes and returns the next token from the deque.
String token = tokens.removeFirst();
// If token is '#', returns null indicating an empty child.
if (token.equals("#")) return null;
// Creates a new TreeNode with the integer value from the token.
TreeNode node = new TreeNode(Integer.parseInt(token));
// Recursively reads left and right children and assigns them.
node.left = read(tokens); node.right = read(tokens);
// Returns the constructed node.
return node;
}
}
// TreeNode: simple binary tree node with value and left/right children
// TreeNode class representing a binary tree node with an integer value and left/right children.
static class TreeNode {
// Fields for node value, left child, and right child.
int val; TreeNode left, right;
// Constructor initializing the node value.
TreeNode(int val) { this.val = val; }
}
// Helper to build a sample tree: 1
// / \
// 2 3
// / / \
// 4 5 6
// Builds a sample binary tree with a predefined structure for demonstration.
static TreeNode buildSample() {
// Creates node with value 1 (root).
TreeNode n1 = new TreeNode(1);
// Creates node with value 2 (left child of root).
TreeNode n2 = new TreeNode(2);
// Creates node with value 3 (right child of root).
TreeNode n3 = new TreeNode(3);
// Creates node with value 4 (left child of node 2).
TreeNode n4 = new TreeNode(4);
// Creates node with value 5 (left child of node 3).
TreeNode n5 = new TreeNode(5);
// Creates node with value 6 (right child of node 3).
TreeNode n6 = new TreeNode(6);
// Links root's left and right children.
n1.left = n2; n1.right = n3;
// Links node 2's left child.
n2.left = n4;
// Links node 3's left and right children.
n3.left = n5; n3.right = n6;
// Returns the root of the constructed tree.
return n1;
}
// Helper to collect preorder values for verification
// Performs preorder traversal, collecting node values into a list for verification.
static void preorder(TreeNode node, List<Integer> list) {
// Base case: if node is null, return without adding.
if (node == null) return;
// Adds the current node's value to the list.
list.add(node.val);
// Recursively traverses the left subtree.
preorder(node.left, list);
// Recursively traverses the right subtree.
preorder(node.right, list);
}
// Main method demonstrating serialization and deserialization round-trip.
public static void main(String[] args) {
// Instantiates the Codec.
Codec codec = new Codec();
// Builds the sample tree.
TreeNode original = buildSample();
// Serializes the original tree to a string.
String serialized = codec.serialize(original);
// Prints the serialized string.
System.out.println("Serialized: " + serialized);
// Deserializes the string back into a tree.
TreeNode restored = codec.deserialize(serialized);
// Creates a list to hold preorder values of the original tree.
List<Integer> originalOrder = new ArrayList<>();
// Creates a list to hold preorder values of the restored tree.
List<Integer> restoredOrder = new ArrayList<>();
// Collects preorder traversal of the original tree.
preorder(original, originalOrder);
// Collects preorder traversal of the restored tree.
preorder(restored, restoredOrder);
// Prints the original tree's preorder sequence.
System.out.println("Original preorder: " + originalOrder);
// Prints the restored tree's preorder sequence.
System.out.println("Restored preorder: " + restoredOrder);
// Checks and prints whether the two preorder sequences match, confirming a successful round-trip.
System.out.println("Round-trip matches: " + originalOrder.equals(restoredOrder));
}
}
Walkthrough
Values alone do not preserve shape, so I emit preorder tokens and a marker for every null child. During decoding, each token either creates a node and recursively fills two children or returns null. The token stream is consumed once, giving a unique round trip. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
O(V + E) time and O(V) space.DFS with an original-node to clone-node map.
Deep-copy every node and edge of a connected undirected graph.
Approach
DFS with an original-node to clone-node map. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
DFS with an IdentityHashMap from original node → clone.
If node is null return null; if already cloned, return the existing clone.
Create a clone, map it immediately (before neighbors), then clone each neighbor and attach.
Return the clone of the start node.
Code
import java.util.*;
/**
* Main entry point demonstrating graph cloning via DFS.
* Nests the original Solution and Node classes exactly as provided.
*/
public class Main {
/**
* Solution class implementing graph cloning using DFS and an IdentityHashMap.
* Maps each original node to its clone to handle cycles and shared neighbors.
*/
static class Solution {
public Node cloneGraph(Node node) {
return clone(node, new IdentityHashMap<>());
}
private Node clone(Node node, Map<Node, Node> copies) {
if (node == null) return null;
if (copies.containsKey(node)) return copies.get(node);
Node copy = new Node(node.val);
copies.put(node, copy);
for (Node neighbor : node.neighbors) copy.neighbors.add(clone(neighbor, copies));
return copy;
}
}
/**
* Node class representing a graph vertex with an integer value and a list of neighbors.
* The neighbors list is initialized to an empty ArrayList.
*/
static class Node {
int val; List<Node> neighbors = new ArrayList<>();
Node(int val) { this.val = val; }
}
/**
* Builds a sample undirected graph:
* 1 -- 2
* | |
* 4 -- 3
* @return the node with value 1 (entry point)
*/
private static Node buildSampleGraph() {
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
Node n4 = new Node(4);
n1.neighbors.add(n2); n1.neighbors.add(n4);
n2.neighbors.add(n1); n2.neighbors.add(n3);
n3.neighbors.add(n2); n3.neighbors.add(n4);
n4.neighbors.add(n1); n4.neighbors.add(n3);
return n1;
}
/**
* Prints the graph starting from the given node using BFS to avoid infinite loops.
* @param start the node to start traversal from
* @param label a label for the output
*/
private static void printGraph(Node start, String label) {
if (start == null) {
System.out.println(label + ": (null)");
return;
}
Set<Node> visited = new HashSet<>();
Queue<Node> q = new LinkedList<>();
q.add(start);
visited.add(start);
System.out.print(label + ": ");
while (!q.isEmpty()) {
Node cur = q.poll();
System.out.print("Node(" + cur.val + ") -> ");
for (Node nb : cur.neighbors) {
System.out.print(nb.val + " ");
if (!visited.contains(nb)) {
visited.add(nb);
q.add(nb);
}
}
System.out.print("; ");
}
System.out.println();
}
public static void main(String[] args) {
// Build original graph
Node original = buildSampleGraph();
printGraph(original, "Original");
// Clone the graph
Solution solver = new Solution();
Node cloned = solver.cloneGraph(original);
printGraph(cloned, "Cloned");
// Verify structural equality (values and topology) but distinct objects
System.out.println("Original entry == Cloned entry? " + (original == cloned)); // should be false
System.out.println("Original entry.val == Cloned entry.val? " + (original.val == cloned.val)); // should be true
// Check a neighbor is a different object but same value
Node origNeighbor = original.neighbors.get(0);
Node cloneNeighbor = cloned.neighbors.get(0);
System.out.println("First neighbor same object? " + (origNeighbor == cloneNeighbor)); // false
System.out.println("First neighbor same value? " + (origNeighbor.val == cloneNeighbor.val)); // true
}
}
Walkthrough
Graphs can cycle, so before exploring neighbors I create and memoize the clone. Every later encounter reuses it. I recursively append the clone of each neighbor, which copies all edges while guaranteeing one new object per original node. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Null graph; one node; self-loop; cycles; duplicate labels.
Watch outs: Keying by label instead of node identity; inserting into the map after recursion; shallow-copying neighbor lists.
D30. Word Ladder
O(N * L^2) time with string construction and O(N) space, for N words of length L.Run BFS over valid one-character mutations.Animated
Return the length of the shortest one-letter transformation sequence from beginWord to endWord, using only dictionary words.
Approach
Run BFS over valid one-character mutations. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
If begin==end return 1; put wordList in a set; if end missing return 0.
BFS from begin; at each level try all 1-letter mutations that remain in the set.
When a mutation equals end, return length+1; remove used words from the set.
If BFS empties without hitting end, return 0.
Code
import java.util.*;
/**
* Main class to demonstrate the Word Ladder (LeetCode 127) solution.
* The Solution class is nested statically as required for a single-file JDoodle program.
*/
public class Main {
/**
* Solution for Word Ladder problem.
* Finds the length of the shortest transformation sequence from begin to end word,
* changing one letter at a time, using only words from wordList.
* Uses BFS with a HashSet for O(1) lookups and removals.
*/
static class Solution {
public int ladderLength(String begin, String end, List<String> words) {
if (begin.equals(end)) return 1;
Set<String> unused = new HashSet<>(words);
if (!unused.contains(end)) return 0;
ArrayDeque<String> queue = new ArrayDeque<>();
queue.add(begin); unused.remove(begin);
int length = 1;
while (!queue.isEmpty()) {
for (int size = queue.size(); size > 0; size--) {
char[] chars = queue.remove().toCharArray();
for (int i = 0; i < chars.length; i++) {
char old = chars[i];
for (char c = 'a'; c <= 'z'; c++) {
if (c == old) continue;
chars[i] = c;
String next = new String(chars);
if (next.equals(end)) return length + 1;
if (unused.remove(next)) queue.add(next);
}
chars[i] = old;
}
}
length++;
}
return 0;
}
}
/**
* Main method to test the Solution with various inputs.
* Prints the length of the shortest word ladder or 0 if impossible.
*/
public static void main(String[] args) {
Solution sol = new Solution();
// Test case 1: classic example, expected 5 (hit -> hot -> dot -> dog -> cog)
List<String> wordList1 = Arrays.asList("hot", "dot", "dog", "lot", "log", "cog");
int result1 = sol.ladderLength("hit", "cog", wordList1);
System.out.println("Test 1 (hit->cog with full list): " + result1);
// Test case 2: end word not in list, expected 0
List<String> wordList2 = Arrays.asList("hot", "dot", "dog", "lot", "log");
int result2 = sol.ladderLength("hit", "cog", wordList2);
System.out.println("Test 2 (hit->cog missing end): " + result2);
// Test case 3: begin equals end, expected 1
List<String> wordList3 = Arrays.asList("a", "b", "c");
int result3 = sol.ladderLength("a", "a", wordList3);
System.out.println("Test 3 (begin==end): " + result3);
// Test case 4: one-letter words differ by one letter, so a->c is a single step, expected 2
List<String> wordList4 = Arrays.asList("a", "b", "c");
int result4 = sol.ladderLength("a", "c", wordList4);
System.out.println("Test 4 (a->c in one step): " + result4);
// Test case 5: end word is in the list but nothing links it to the begin word, expected 0
List<String> wordList5 = Arrays.asList("cog", "xyz");
int result5 = sol.ladderLength("hit", "cog", wordList5);
System.out.println("Test 5 (no connecting chain): " + result5);
}
}
Walkthrough
I treat each dictionary word as a graph node and one-character changes as edges. BFS explores sequences in increasing length, so the first time I generate the end word is optimal. Removing words when enqueued prevents repeated work. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: End absent; begin equals end contract; mixed word lengths; repeated discovery.
Watch outs: Using DFS for shortest path; marking visited on dequeue; omitting begin/end counting.
Ready
D31. Rotting Oranges
O(rows cols) time and O(rows cols) space.Use multi-source BFS seeded with every initially rotten orange.Animated
In a grid, each rotten orange infects adjacent fresh oranges each minute; return minutes until none are fresh, or -1.
Approach
Use multi-source BFS seeded with every initially rotten orange. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Count fresh oranges; enqueue every initially rotten cell.
Multi-source BFS by minutes: for each cell in the current queue, rot adjacent fresh cells and enqueue them.
Each full queue layer increments minutes by 1.
If fresh remain when the queue empties return -1; else return minutes.
Code
// Imports Java utility classes like ArrayDeque for BFS queue.
import java.util.*;
/**
* Main class to demonstrate the oranges rotting BFS solution.
*/
// Main class containing the solution and test cases.
public class Main {
/**
* Solution class containing the orangesRotting method.
* Uses multi-source BFS to simulate the rotting process minute by minute.
*/
// Static inner class Solution encapsulating the orangesRotting algorithm.
static class Solution {
// Method orangesRotting takes a grid and returns minutes until all oranges rot, or -1 if impossible.
public int orangesRotting(int[][] grid) {
// Edge case: empty grid returns 0 minutes.
if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;
// Initialize dimensions, fresh orange count, and elapsed minutes.
int rows = grid.length, cols = grid[0].length, fresh = 0, minutes = 0;
// Queue for BFS stores coordinates of rotten oranges.
ArrayDeque<int[]> queue = new ArrayDeque<>();
// Scan grid to find initial rotten oranges (value 2) and count fresh oranges (value 1).
for (int r = 0; r < rows; r++) for (int c = 0; c < cols; c++)
// Add rotten oranges to queue; increment fresh count for fresh oranges.
if (grid[r][c] == 2) queue.add(new int[]{r,c}); else if (grid[r][c] == 1) fresh++;
// Direction vectors for up, down, right, left neighbors.
int[][] d = {{1,0},{-1,0},{0,1},{0,-1}};
// Loop while there are fresh oranges and queue not empty (BFS levels).
while (fresh > 0 && !queue.isEmpty()) {
// Process current level: iterate over all rotten oranges at this minute.
for (int size = queue.size(); size > 0; size--) {
// Dequeue a rotten orange cell.
int[] cell = queue.remove();
// Check all four neighboring cells.
for (int[] x : d) {
// Compute neighbor coordinates.
int r = cell[0] + x[0], c = cell[1] + x[1];
// If neighbor is within bounds and fresh, rot it.
if (r >= 0 && r < rows && c >= 0 && c < cols && grid[r][c] == 1) {
// Mark neighbor rotten, decrease fresh count, enqueue for next minute.
grid[r][c] = 2; fresh--; queue.add(new int[]{r,c});
}
}
// End of processing current level.
}
// Increment minute counter after each BFS level.
minutes++;
}
// Return minutes if all fresh oranges rotted, else -1.
return fresh == 0 ? minutes : -1;
}
}
// Main method runs test cases to verify solution.
public static void main(String[] args) {
// Create Solution instance.
Solution sol = new Solution();
// Test case 1: Basic case from LeetCode
// Define grid for test case 1.
int[][] grid1 = {
{2,1,1},
{1,1,0},
{0,1,1}
};
// Print result for test 1.
System.out.println("Test 1 - Expected 4: " + sol.orangesRotting(grid1));
// Test case 2: Impossible case (fresh orange can't be reached)
// Define grid for test case 2.
int[][] grid2 = {
{2,1,1},
{0,1,1},
{1,0,1}
};
// Print result for test 2.
System.out.println("Test 2 - Expected -1: " + sol.orangesRotting(grid2));
// Test case 3: No fresh oranges
// Define grid for test case 3.
int[][] grid3 = {
{0,2}
};
// Print result for test 3.
System.out.println("Test 3 - Expected 0: " + sol.orangesRotting(grid3));
// Test case 4: Empty grid
// Define empty grid for test case 4.
int[][] grid4 = {};
// Print result for test 4.
System.out.println("Test 4 - Expected 0: " + sol.orangesRotting(grid4));
// Test case 5: All fresh, no rotten
// Define grid for test case 5.
int[][] grid5 = {
{1,1},
{1,1}
};
// Print result for test 5.
System.out.println("Test 5 - Expected -1: " + sol.orangesRotting(grid5));
}
}
Walkthrough
All initially rotten oranges enter the queue at time zero, making this multi-source BFS. Each level infects exactly the next-minute frontier. I decrement a fresh count on first infection; after BFS, zero means the elapsed levels are sufficient, otherwise some fresh orange was unreachable. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: No fresh oranges; no rotten sources; unreachable fresh cells; one cell.
Watch outs: Running BFS separately per source; off-by-one minutes; counting infected cells more than once.
Ready
D32. Median of Two Sorted Arrays
O(log(min(m,n))) time and O(1) space.Binary-search a partition of the shorter array so left halves contain half the values and all left v
Return the median of two sorted arrays in O(log(min(m,n))) time.
Approach
Binary-search a partition of the shorter array so left halves contain half the values and all left values are <= all right values. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Always binary-search the shorter array; ensure a is shorter.
Choose cut cutA in a; set cutB so left halves have (m+n+1)/2 elements.
If aL≤bR and bL≤aR, partition is correct: odd length → max(left); even → average of max(left) and min(right).
If aL > bR, move cut left; else move cut right. Repeat until found.
Code
import java.util.*;
// Main class containing the solution and demo
public class Main {
// Solution class for finding median of two sorted arrays using binary search
static class Solution {
public double findMedianSortedArrays(int[] a, int[] b) {
if (a.length > b.length) return findMedianSortedArrays(b, a);
int m = a.length, n = b.length, lo = 0, hi = m;
if (m + n == 0) throw new IllegalArgumentException("both arrays are empty");
while (lo <= hi) {
int cutA = (lo + hi) >>> 1;
int cutB = (m + n + 1) / 2 - cutA;
int aL = cutA == 0 ? Integer.MIN_VALUE : a[cutA - 1];
int aR = cutA == m ? Integer.MAX_VALUE : a[cutA];
int bL = cutB == 0 ? Integer.MIN_VALUE : b[cutB - 1];
int bR = cutB == n ? Integer.MAX_VALUE : b[cutB];
if (aL <= bR && bL <= aR) {
if (((m + n) & 1) == 1) return Math.max(aL, bL);
return ((long)Math.max(aL, bL) + Math.min(aR, bR)) / 2.0;
}
if (aL > bR) hi = cutA - 1; else lo = cutA + 1;
}
throw new IllegalArgumentException("both arrays cannot be empty or unsorted");
}
}
// Main method to demonstrate the median finding algorithm
public static void main(String[] args) {
Solution sol = new Solution(); // Create solution instance
// Test case 1: odd total length
int[] nums1 = {1, 3};
int[] nums2 = {2};
double median1 = sol.findMedianSortedArrays(nums1, nums2); // Expected: 2.0
System.out.println("Test 1 - Median of [1,3] and [2]: " + median1);
// Test case 2: even total length
int[] nums3 = {1, 2};
int[] nums4 = {3, 4};
double median2 = sol.findMedianSortedArrays(nums3, nums4); // Expected: 2.5
System.out.println("Test 2 - Median of [1,2] and [3,4]: " + median2);
// Test case 3: one empty array
int[] nums5 = {};
int[] nums6 = {1, 2, 3, 4, 5};
double median3 = sol.findMedianSortedArrays(nums5, nums6); // Expected: 3.0
System.out.println("Test 3 - Median of [] and [1,2,3,4,5]: " + median3);
// Test case 4: arrays with different lengths
int[] nums7 = {1, 2, 3, 4, 5, 6};
int[] nums8 = {7, 8, 9, 10};
double median4 = sol.findMedianSortedArrays(nums7, nums8); // Expected: 5.5
System.out.println("Test 4 - Median of [1,2,3,4,5,6] and [7,8,9,10]: " + median4);
// Test case 5: single element arrays
int[] nums9 = {5};
int[] nums10 = {6};
double median5 = sol.findMedianSortedArrays(nums9, nums10); // Expected: 5.5
System.out.println("Test 5 - Median of [5] and [6]: " + median5);
}
}
Walkthrough
I binary-search only the shorter array's cut and derive the other cut so the left side has the required size. If a left boundary exceeds the opposite right boundary, I move the cut accordingly. At the valid partition, the middle boundary values directly give the odd or even median. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: One array empty; total size one; duplicates; very different sizes; integer overflow when averaging.
O(limit) total work and O(1) shared state.Guard a shared counter with one lock and condition; each thread waits for its turn.
Use three threads to print 1,2,3,4,...,limit in strict order, with thread A printing 1 mod 3, B 2 mod 3, and C 0 mod 3.
Approach
Guard a shared counter with one lock and condition; each thread waits for its turn. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Shared next counter under one lock + condition; threads take residues 1,2,0 for turns.
Each thread: while next ≤ limit, await until next % 3 == residue.
Print next, increment it, signalAll.
Exit when next > limit, signalling others to wake and finish.
Code
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
// Main class containing the complete program
public class Main {
// OrderedPrinter coordinates three threads to print numbers 1..limit in order
// Each thread prints numbers with a specific residue modulo 3 (1, 2, or 0)
static class OrderedPrinter {
private final Lock lock = new ReentrantLock();
private final Condition changed = lock.newCondition();
private final int limit;
private int next = 1;
OrderedPrinter(int limit) { this.limit = limit; }
public void printFor(int residue) throws InterruptedException {
lock.lockInterruptibly();
try {
while (next <= limit) {
while (next <= limit && next % 3 != residue) changed.await();
if (next <= limit) {
System.out.println(next++);
changed.signalAll();
}
}
changed.signalAll();
} finally { lock.unlock(); }
}
}
// Entry point: creates printer, submits three tasks, waits for completion
public static void main(String[] args) throws Exception {
// Create printer for numbers 1 through 10
OrderedPrinter printer = new OrderedPrinter(10);
// Create executor with 3 threads
ExecutorService executor = Executors.newFixedThreadPool(3);
// Create three callable tasks for residues 1, 2, 0
Callable<Void> task1 = () -> { printer.printFor(1); return null; };
Callable<Void> task2 = () -> { printer.printFor(2); return null; };
Callable<Void> task0 = () -> { printer.printFor(0); return null; };
// Submit all tasks and wait for completion
executor.invokeAll(List.of(task1, task2, task0));
// Shutdown executor
executor.shutdown();
}
}
Walkthrough
The counter is shared under one lock. Each worker waits in a while loop until the counter's modulo identifies that worker, prints and increments once, then signals all. The while loop handles spurious wakeups, and a final signal ensures no worker remains blocked after the limit. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Limit zero; interruption; spurious wakeups; termination while other threads wait.
Watch outs: Using if instead of while; failing to signal on termination; sleeping for coordination; swallowing interrupts.
D34. Subarray Sum Equals K
O(n) expected time and O(n) space.Use prefix sums and a frequency map of previously seen prefixes.Animated
Count contiguous subarrays whose sum equals k.
Approach
Use prefix sums and a frequency map of previously seen prefixes. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
Seed freq map with {0:1}; prefix=0, count=0.
For each value: add to prefix; add freq[prefix-k] to count.
Increment freq[prefix].
Return count.
Code
import java.util.*;
/**
* Main entry point for demonstrating the subarray sum solution.
*/
public class Main {
/**
* Solution class that counts contiguous subarrays whose sum equals k
* using prefix sums and a frequency map of previously seen prefixes.
*/
static class Solution {
/**
* Returns the number of contiguous subarrays whose sum equals k.
* @param nums input array of integers
* @param k target sum (long to avoid overflow)
* @return count of subarrays with sum exactly k
*/
public long subarraySum(int[] nums, long k) {
Map<Long, Long> seen = new HashMap<>();
seen.put(0L, 1L); // seed frequency map with prefix sum 0 occurring once
long prefix = 0; // running prefix sum
long count = 0; // result accumulator
for (int x : nums) { // iterate through each element
prefix += x; // update prefix sum
count += seen.getOrDefault(prefix - k, 0L); // add number of previous prefixes that would make sum k
seen.merge(prefix, 1L, Long::sum); // increment frequency of current prefix
}
return count;
}
}
public static void main(String[] args) {
Solution sol = new Solution();
// Test case 1: basic example
int[] nums1 = {1, 1, 1};
long k1 = 2;
System.out.println("Test 1: nums=[1,1,1], k=2 -> " + sol.subarraySum(nums1, k1) + " (expected 2)");
// Test case 2: multiple subarrays
int[] nums2 = {1, 2, 3};
long k2 = 3;
System.out.println("Test 2: nums=[1,2,3], k=3 -> " + sol.subarraySum(nums2, k2) + " (expected 2)");
// Test case 3: negative numbers
int[] nums3 = {-1, -1, 1};
long k3 = 0;
System.out.println("Test 3: nums=[-1,-1,1], k=0 -> " + sol.subarraySum(nums3, k3) + " (expected 1)");
// Test case 4: empty array
int[] nums4 = {};
long k4 = 5;
System.out.println("Test 4: nums=[], k=5 -> " + sol.subarraySum(nums4, k4) + " (expected 0)");
// Test case 5: large numbers to verify long usage
int[] nums5 = {1000000, 1000000, 1000000};
long k5 = 2000000L;
System.out.println("Test 5: large numbers -> " + sol.subarraySum(nums5, k5) + " (expected 2)");
}
}
Walkthrough
Let prefix be the sum through the current index. Any earlier prefix equal to prefix-k defines a subarray summing to k. I store how often each prefix occurred, seed zero once for subarrays starting at index zero, add the complement count, then record the current prefix. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Edges: Empty array; k=0; zeros; negative values; sum overflow.
Watch outs: Using sliding window despite negatives; forgetting initial prefix zero; storing only existence instead of frequency.
Ready
D35. Bounded Producer-Consumer Queue
O(1) per operation and O(capacity) space.Use one lock with notFull and notEmpty conditions around a deque.
Implement a bounded blocking queue with thread-safe put and take operations.
Approach
Use one lock with notFull and notEmpty conditions around a deque. State the invariant before coding, then explain why every element/state is processed only as often as the complexity claims.
Easy Algorithm
One lock with notFull / notEmpty conditions around a capacity-bounded deque.
put: while full await notFull; append; signal notEmpty.
take: while empty await notEmpty; remove front; signal notFull.
Always unlock in finally; reject null puts per policy.
Code
import java.util.*;
import java.util.concurrent.locks.*;
/**
* Main class containing the BoundedBuffer demonstration.
*/
public class Main {
/**
* A thread-safe bounded buffer using a single lock with two conditions
* (notFull and notEmpty) around a capacity-bounded deque.
* put: waits while full, then appends and signals notEmpty.
* take: waits while empty, then removes front and signals notFull.
* Always unlocks in finally; rejects null puts.
*/
static class BoundedBuffer<T> {
private final Deque<T> queue = new ArrayDeque<>(); // underlying double-ended queue
private final int capacity; // maximum number of elements allowed
private final Lock lock = new ReentrantLock(); // mutual exclusion lock
private final Condition notEmpty = lock.newCondition(), notFull = lock.newCondition(); // wait conditions
BoundedBuffer(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException("capacity"); // validate capacity
this.capacity = capacity;
}
public void put(T item) throws InterruptedException {
Objects.requireNonNull(item); // reject null per policy
lock.lockInterruptibly(); // acquire lock, respond to interruption
try {
while (queue.size() == capacity) notFull.await(); // wait while full
queue.addLast(item); notEmpty.signal(); // append item, wake one taker
} finally { lock.unlock(); } // always release lock
}
public T take() throws InterruptedException {
lock.lockInterruptibly(); // acquire lock, respond to interruption
try {
while (queue.isEmpty()) notEmpty.await(); // wait while empty
T item = queue.removeFirst(); notFull.signal(); return item; // remove front, wake one putter, return item
} finally { lock.unlock(); } // always release lock
}
}
/**
* Demonstrates BoundedBuffer with single-threaded put/take calls within capacity.
* Puts 3 items into a capacity-3 buffer, then takes 3 items.
*/
public static void main(String[] args) throws Exception {
// Create a buffer with capacity 3
BoundedBuffer<String> buffer = new BoundedBuffer<>(3);
// Put three items (within capacity, so no blocking)
buffer.put("first");
buffer.put("second");
buffer.put("third");
System.out.println("Put 3 items into buffer");
// Take three items (buffer has items, so no blocking)
String item1 = buffer.take();
String item2 = buffer.take();
String item3 = buffer.take();
System.out.println("Took items: " + item1 + ", " + item2 + ", " + item3);
}
}
Walkthrough
I protect the deque with one lock and use separate conditions for full and empty states. Put waits in a while loop while full, inserts, then signals a consumer. Take waits while empty, removes, then signals a producer. The loops handle spurious wakeups and preserve the capacity invariant. I would then walk through the example, state the invariant, give the complexity, and test the most failure-prone edge case before coding.
Watch outs: Using if around await; signaling the wrong condition; mutating outside the lock; swallowing interruption.
D36. Jump Game II
Greedy BFS-on-array — track current jump end and farthest reachAnimated
Given nums where each index is the max jump length from that position, return the minimum jumps to reach the last index (guaranteed reachable in the classic OA variant).
Approach
Imagine the array as an unweighted graph (edges from i to i+1 … i+nums[i]). Shortest path is BFS by “levels,” where a level = all indices reachable with the same jump count. You do not need a queue: scan left→right, keep farthest inside the current level, and when the scan hits currentEnd, take one jump and extend the end to farthest.
Easy Algorithm
If length ≤ 1, answer is 0.
Set jumps=0, currentEnd=0, farthest=0.
For each index i from 0 to n-2:
Return jumps.
Code
int jump(int[] nums) {
int jumps = 0, end = 0, farthest = 0;
for (int i = 0; i < nums.length - 1; i++) {
farthest = Math.max(farthest, i + nums[i]);
if (i == end) {
jumps++;
end = farthest;
}
}
return jumps;
}
Walkthrough
Input:[2,3,1,1,4]
| i | nums[i] | farthest | end | jumps | note |
|---|---------|----------|-----|-------|------|
| 0 | 2 | 2 | 0→2 | 1 | end of jump 1 |
| 1 | 3 | max(2,4)=4 | 2 | 1 | |
| 2 | 1 | 4 | 2→4 | 2 | end of jump 2; can reach last |
Output:2 (0→1→4).
---
Ready
D37. Trapping Rain Water
Two pointers with leftMax / rightMax (monotonic stack alternative)Animated
Given elevation map height[i], compute how much water can be trapped after raining.
Approach
Water above index i equals min(maxLeft, maxRight) - height[i] (or 0). Two pointers start at ends; always advance the side with the smaller height/max because that side’s bound is known relative to the taller opposite wall.
Easy Algorithm
lo=0, hi=n-1, leftMax=rightMax=water=0.
While lo < hi:
Return water.
Code
int trap(int[] height) {
int lo = 0, hi = height.length - 1, leftMax = 0, rightMax = 0, water = 0;
while (lo < hi) {
if (height[lo] < height[hi]) {
leftMax = Math.max(leftMax, height[lo]);
water += leftMax - height[lo];
lo++;
} else {
rightMax = Math.max(rightMax, height[hi]);
water += rightMax - height[hi];
hi--;
}
}
return water;
}
Walkthrough
Input:[0,1,0,2,1,0,1,3,2,1,2,1] → trapped units = 6.
Bars of height 2 and 3 form basins; water sits in the valleys between them. Trace with two pointers: the lower side accumulates until the opposite max protects it.
---
Ready
D38. Java Stream Maximum (comparator follow-ups)
Streams + Comparator chaining; empty handling
Return the maximum element from a list using the Stream API; follow-up: max by a field with deterministic tie-break.
Approach
stream().max(comparator) is a clear reduction. It is not asymptotically faster than a loop — interviewers want API fluency, empty handling (Optional), and tie-breaking via thenComparing. Avoid parallelStream() unless you can justify spliterator cost and pool contention.
Easy Algorithm
Confirm empty-list behavior (throw vs Optional empty).
For primitives/boxed ints: Integer::compareTo (or Comparator.naturalOrder()).
For objects: Comparator.comparingLong(Employee::salary).thenComparing(Employee::id).
Unwrap with orElseThrow if contract forbids empty.
Code
import java.util.List;
import java.util.Comparator;
// Main class containing the program entry point and helper classes.
public class Main {
// Employee record to hold employee data
// Record representing an employee with id, name, and salary.
static record Employee(int id, String name, long salary) {}
// Solution class containing the two methods from the snippet
// Solution class containing methods for finding max value and highest paid employee.
static class Solution {
// Returns the maximum integer value from a list, throws if empty
// Method to find the maximum integer in a list, throws exception if list is empty.
int maxValue(List<Integer> values) {
// Uses Java Stream API to find the maximum value.
return values.stream()
.max(Integer::compareTo)
.orElseThrow(() -> new IllegalArgumentException("empty list"));
}
// Returns the highest paid employee, using salary then id for tie-breaking
// Method to find the employee with the highest salary, using id as tie-breaker.
Employee highestPaid(List<Employee> employees) {
// Uses stream with a comparator that compares by salary then id.
return employees.stream()
.max(Comparator.comparingLong(Employee::salary)
.thenComparing(Employee::id))
.orElseThrow();
}
}
// Main method demonstrating the Solution methods.
public static void main(String[] args) {
// Creates an instance of Solution.
Solution sol = new Solution();
// Demo maxValue with a non-empty list
// Creates a list of integers for maxValue demo.
List<Integer> numbers = List.of(3, 1, 4, 1, 5, 9);
// Calls maxValue and stores the result.
int max = sol.maxValue(numbers);
// Prints the maximum value.
System.out.println("Max value: " + max);
// Demo highestPaid with a non-empty list of employees
// Creates a list of Employee records for highestPaid demo.
List<Employee> staff = List.of(
new Employee(1, "Alice", 50000),
new Employee(2, "Bob", 60000),
new Employee(3, "Carol", 60000) // same salary as Bob, higher id
);
// Calls highestPaid to get the top employee.
Employee top = sol.highestPaid(staff);
// Prints the highest paid employee details.
System.out.println("Highest paid: " + top.name() + " (id=" + top.id() + ", salary=" + top.salary() + ")");
}
}
Walkthrough
values=[3,1,4,1,5] → max Optional packs 5.
employees with salaries (A:100,id=2), (B:100,id=1) → tie on salary, pick lower id B if thenComparing(Employee::id).
---
D39. Binary Search Template — first index
Binary search on answer / lower bound
Find the first index (or value) where a monotonic condition(mid) becomes true.
Approach
If condition flips from false→true exactly once, binary search finds the leftmost true. Keep answer when true and search left; otherwise search right. Prove monotonicity before coding.
Easy Algorithm
Set inclusive [low, high]; answer = high+1 (sentinel = none).
While low <= high: mid = low + (high-low)/2.
If condition(mid): answer=mid; high=mid-1.
Else low=mid+1.
Return answer.
Code
int firstTrue(int low, int high) { // inclusive; answer may be high+1
int answer = high + 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (condition(mid)) {
answer = mid;
high = mid - 1;
} else {
low = mid + 1;
}
}
return answer;
}
Walkthrough
Search first index with a[i] >= 4 in [1,2,4,4,5]:
mid path lands on index 2; searching left confirms 2 is first → 2.
---
D40. Prefix Sum + Frequency — Subarray Sum K (classic form)
Prefix sum + hashmap of frequencies
Count subarrays whose sum equals k (negatives allowed).
Approach
prefix[j] - prefix[i] = k ⇒ look up how many prior prefixes equal prefix - k. Sliding window fails with negatives. Seed map with {0:1}.
Easy Algorithm
freq={0:1}, prefix=0, answer=0.
For each value: add to prefix; add freq[prefix-k]; increment freq[prefix].
Return answer.
Code
import java.util.*;
/**
* Main class to demonstrate the subarray sum equals k algorithm.
* Uses prefix sum with a frequency hashmap to count subarrays.
*/
public class Main {
/**
* Solution class containing the subarraySum method.
* Counts subarrays whose sum equals k (negatives allowed).
*/
static class Solution {
// Original snippet method - counts subarrays with sum == k
int subarraySum(int[] nums, int k) {
Map<Long, Integer> frequency = new HashMap<>();
frequency.put(0L, 1);
long prefix = 0;
int answer = 0;
for (int value : nums) {
prefix += value;
answer += frequency.getOrDefault(prefix - k, 0);
frequency.merge(prefix, 1, Integer::sum);
}
return answer;
}
}
public static void main(String[] args) {
Solution sol = new Solution();
// Test case 1: Basic example with positive numbers
int[] nums1 = {1, 1, 1};
int k1 = 2;
System.out.println("Test 1: nums=" + Arrays.toString(nums1) + ", k=" + k1);
System.out.println("Subarrays with sum " + k1 + ": " + sol.subarraySum(nums1, k1)); // Expected: 2
// Test case 2: Array with negative numbers
int[] nums2 = {1, 2, 3, -2, 5};
int k2 = 5;
System.out.println("\nTest 2: nums=" + Arrays.toString(nums2) + ", k=" + k2);
System.out.println("Subarrays with sum " + k2 + ": " + sol.subarraySum(nums2, k2)); // Expected: 2 ([2,3] and [5])
// Test case 3: All zeros
int[] nums3 = {0, 0, 0, 0};
int k3 = 0;
System.out.println("\nTest 3: nums=" + Arrays.toString(nums3) + ", k=" + k3);
System.out.println("Subarrays with sum " + k3 + ": " + sol.subarraySum(nums3, k3)); // Expected: 10
// Test case 4: Single element equals k
int[] nums4 = {5};
int k4 = 5;
System.out.println("\nTest 4: nums=" + Arrays.toString(nums4) + ", k=" + k4);
System.out.println("Subarrays with sum " + k4 + ": " + sol.subarraySum(nums4, k4)); // Expected: 1
// Test case 5: No subarray matches
int[] nums5 = {1, 2, 3};
int k5 = 7;
System.out.println("\nTest 5: nums=" + Arrays.toString(nums5) + ", k=" + k5);
System.out.println("Subarrays with sum " + k5 + ": " + sol.subarraySum(nums5, k5)); // Expected: 0
}
}
Walkthrough
nums=[1,2,3], k=3: prefixes 1,3,6. At prefix 3, 3-3=0 hits seed → count 1 ([1,2]); also later [3] → 2.
---
D41. Top K Frequent — bounded min heap (classic)
Frequency map + size-k min-heap (or bucket sort)
Return the k most frequent elements (any order among ties unless specified).
Approach
Count frequencies, keep a min-heap of size k keyed by frequency so the root is the least frequent of the current top-k. Bucket sort is O(n) when frequencies ≤ n.
Easy Algorithm
Build count map.
Push keys into min-heap by frequency; if size > k, poll.
Drain heap into result array.
Code
import java.util.*;
public class Main {
/**
* Solution class containing the method to find the k most frequent elements in an array.
*/
static class Solution {
/**
* Returns the k most frequent elements from the input array.
* Uses a HashMap to count frequencies and a min-heap of size k to track top elements.
*
* @param nums input array of integers
* @param k number of top frequent elements to return
* @return array of k most frequent elements
*/
int[] topKFrequent(int[] nums, int k) {
// Map each value to its frequency count
Map<Integer, Integer> count = new HashMap<>();
for (int value : nums) count.merge(value, 1, Integer::sum);
// Min-heap ordered by frequency (ascending) so the smallest frequency stays at the top
PriorityQueue<Integer> heap =
new PriorityQueue<>(Comparator.comparingInt(count::get));
// Iterate over unique values; maintain heap size <= k
for (int value : count.keySet()) {
heap.offer(value); // add current value
if (heap.size() > k) heap.poll(); // remove least frequent if over capacity
}
// Extract elements from heap into result array in descending frequency order
int[] answer = new int[k];
for (int i = k - 1; i >= 0; i--) answer[i] = heap.poll();
return answer;
}
}
/**
* Main method demonstrating the topKFrequent function with two example cases.
*/
public static void main(String[] args) {
Solution sol = new Solution();
// Example 1: frequencies 1→3, 2→2, 3→1; top 2 are 1 and 2 (no tie at cutoff)
int[] nums1 = {1, 1, 1, 2, 2, 3};
int k1 = 2;
int[] result1 = sol.topKFrequent(nums1, k1);
System.out.print("Example 1: ");
for (int v : result1) System.out.print(v + " ");
System.out.println();
// Example 2: frequencies 4→3, 5→2, 6→4; top 2 are 6 and 4
int[] nums2 = {4, 4, 4, 5, 5, 6, 6, 6, 6};
int k2 = 2;
int[] result2 = sol.topKFrequent(nums2, k2);
System.out.print("Example 2: ");
for (int v : result2) System.out.print(v + " ");
System.out.println();
}
}
Compute shortest distances from source on a directed graph with non-negative edge weights.
Approach
Repeatedly settle the unsettled node with smallest tentative distance; relax outgoing edges. Skip stale PQ entries when dist != distance[node]. Never use Dijkstra with negative weights (Bellman-Ford / SPFA instead).
For each edge, if dist+w < distance[next], update and push.
Return distance.
Code
import java.util.*;
/**
* Main class to demonstrate Dijkstra's algorithm on a sample graph.
*/
public class Main {
/**
* Solution class containing the Dijkstra shortest path method.
*/
static class Solution {
// Original dijkstra method verbatim from the course snippet
/**
* Computes shortest path distances from a source node to all other nodes
* using Dijkstra's algorithm with a priority queue.
* @param graph adjacency list where each entry is [neighbor, weight]
* @param source starting node index
* @return array of distances, Long.MAX_VALUE for unreachable nodes
*/
long[] dijkstra(List<List<int[]>> graph, int source) {
// Initialize distance array with infinity
long[] distance = new long[graph.size()];
Arrays.fill(distance, Long.MAX_VALUE);
// Distance to source is zero
distance[source] = 0;
// Min-heap prioritized by current known distance
PriorityQueue<long[]> queue =
new PriorityQueue<>(Comparator.comparingLong(a -> a[0]));
// Start with source node at distance 0
queue.offer(new long[]{0, source});
// Process nodes in order of increasing distance
while (!queue.isEmpty()) {
// Extract node with smallest tentative distance
long[] current = queue.poll();
long dist = current[0];
int node = (int) current[1];
// Skip stale entries (outdated distance)
if (dist != distance[node]) continue;
// Relax all outgoing edges
for (int[] edge : graph.get(node)) { // [neighbor, weight]
int next = edge[0], weight = edge[1];
long candidate = dist + weight;
// Update if a shorter path is found
if (candidate < distance[next]) {
distance[next] = candidate;
queue.offer(new long[]{candidate, next});
}
}
}
return distance;
}
}
/**
* Entry point: builds a sample graph, runs Dijkstra from node 0, and prints results.
*/
public static void main(String[] args) {
// Create a sample directed graph with 6 nodes (0-5)
int n = 6;
List<List<int[]>> graph = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
graph.add(new ArrayList<>());
}
// Add edges: [neighbor, weight]
graph.get(0).add(new int[]{1, 4});
graph.get(0).add(new int[]{2, 1});
graph.get(1).add(new int[]{3, 1});
graph.get(2).add(new int[]{1, 2});
graph.get(2).add(new int[]{3, 5});
graph.get(3).add(new int[]{4, 3});
// Node 5 is isolated (unreachable)
Solution solver = new Solution();
long[] distances = solver.dijkstra(graph, 0);
// Print distances, showing "INF" for unreachable nodes
System.out.println("Shortest distances from node 0:");
for (int i = 0; i < distances.length; i++) {
if (distances[i] == Long.MAX_VALUE) {
System.out.println("Node " + i + ": INF");
} else {
System.out.println("Node " + i + ": " + distances[i]);
}
}
}
}
Support union / find for undirected connectivity, cycle detection, and component counts.
Approach
Each set has a representative. Path compression flattens trees on find; union by size keeps trees shallow → nearly O(1) amortized.
Easy Algorithm
Init parent[i]=i, size[i]=1.
find: recurse/compress to root.
union: if different roots, attach smaller to larger; return whether a merge happened.
Code
class UnionFind {
int[] parent, size;
UnionFind(int n) {
parent = new int[n]; size = new int[n];
for (int i = 0; i < n; i++) { parent[i] = i; size[i] = 1; }
}
int find(int x) {
if (parent[x] != x) parent[x] = find(parent[x]);
return parent[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];
return true;
}
}
Walkthrough
union(0,1), union(1,2) → one component {0,1,2}. union(0,2) returns false (already connected = cycle if building a graph).
---
D44. Backtracking Template
Choose → explore → unchoose; prune duplicates
Enumerate combinations/subsets under constraints (e.g., sum to target) without duplicate multisets.
Approach
DFS builds a path. Recurse with i (reuse allowed) or i+1 (no reuse). Sort first so equal candidates are adjacent; skip i>start && a[i]==a[i-1] to avoid duplicate branches.
Easy Algorithm
Sort candidates.
If remaining==0, record a copy of path.
For i from start while candidates[i] <= remaining:
Code
import java.util.*;
/**
* Main entry point demonstrating the combination sum II algorithm.
* Finds all unique combinations in candidates that sum to target.
*/
public class Main {
/**
* Solution class containing the backtracking search method.
* This is a direct translation of the course snippet.
*/
static class Solution {
/**
* Recursive backtracking search for combinations that sum to target.
* @param start index to start searching from (avoids reuse of earlier elements)
* @param candidates sorted array of candidate numbers
* @param path current combination being built
* @param remaining remaining sum needed to reach target
* @param answer list to collect all valid combinations
*/
void search(int start, int[] candidates, List<Integer> path,
int remaining, List<List<Integer>> answer) {
if (remaining == 0) { // found a valid combination
answer.add(new ArrayList<>(path)); // store a copy of the current path
return;
}
for (int i = start; i < candidates.length && candidates[i] <= remaining; i++) {
if (i > start && candidates[i] == candidates[i - 1]) continue; // skip duplicates
path.add(candidates[i]); // choose candidate[i]
search(i + 1, candidates, path, remaining - candidates[i], answer); // explore with next index
path.remove(path.size() - 1); // unchoose candidate[i] (backtrack)
}
}
}
public static void main(String[] args) {
int[] candidates = {10, 1, 2, 7, 6, 1, 5}; // sample input with duplicates
int target = 8;
Arrays.sort(candidates); // sort to group duplicates together
List<List<Integer>> answer = new ArrayList<>();
new Solution().search(0, candidates, new ArrayList<>(), target, answer);
System.out.println("Combinations summing to " + target + ":");
for (List<Integer> comb : answer) {
System.out.println(comb); // print each combination
}
}
}
Walkthrough
candidates=[2,3,3,6], target=6, no reuse: paths include [3,3] and [6]; duplicate 3s pruned so [3,3] appears once.
---
## 5. Core reusable templates
The D38–D44 entries above are the reusable templates (streams, binary search, prefix+hash, top-k heap, Dijkstra, Union-Find, backtracking). Prefer those canonical 4-part writeups during revision.
## 6. Complexity cheat sheet
- Hash map/set lookup: O(1) average, O(n) pathological.
- Sort: O(n log n).
- Heap insert/remove: O(log n); peek O(1).
- BFS/DFS graph: O(V+E).
- Tree traversal: O(n), recursion stack O(h).
- Binary search: O(log n).
- Sliding window/two pointers: usually O(n) when each pointer only moves forward.
- Monotonic stack: O(n) amortized because each item enters/leaves once.
- DP: number of states × transition cost.
- Segment tree: build O(n), point update/range query O(log n).
- Trie
D45. Two Sum
O(n)/O(n)Hash map complementJava + JS lab
Return indices of two numbers that add to target.
Approach
One-pass map value→index; probe for complement.
Easy Algorithm
Map value→index.
If complement seen return [j,i] else store.
Code (Java)
import java.util.*;
/**
* Demo wrapper for the course's twoSum snippet. Solution is nested so the
* whole program fits in one file; the snippet's own main prints the lesson example.
*/
public class Main {
/**
* One-pass hash map solution: for each number, look up whether its complement
* (target - number) was already seen; if so return both indices.
*/
static class Solution {
public int[] twoSum(int[] nums,int target){Map<Integer,Integer> m=new HashMap<>();
for(int i=0;i<nums.length;i++){Integer j=m.get(target-nums[i]);if(j!=null)return new int[]{j,i};m.put(nums[i],i);}
throw new IllegalArgumentException("none");}
public static void main(String[] args) {
System.out.println(Arrays.toString(new Solution().twoSum(new int[]{2,7,11,15},9)));
}
}
public static void main(String[] args) {
// Lesson example: 2 + 7 = 9, so the indices are [0, 1]
Solution.main(args);
// Complement found later in the array: 3 + 4 = 7 at indices [1, 2]
System.out.println(Arrays.toString(new Solution().twoSum(new int[]{5, 3, 4}, 7)));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[2,7,11,15],9 → [0,1]
Also asked: Amazon OA, Target
D46. Product of Array Except Self
O(n)/O(1)Prefix + suffixJava + JS lab
Product of all elements except self (no division).
Approach
Left products into ans; multiply running right product.
Easy Algorithm
Left pass into ans.
Right pass multiply suffix.
Code (Java)
import java.util.*;
/**
* Demo wrapper for the course's productExceptSelf snippet. Solution is nested so the
* whole program fits in one file; the snippet's own main prints the lesson example.
*/
public class Main {
/**
* Builds the answer in two passes without division: first each slot holds the product
* of everything to its left, then a running product of everything to its right is multiplied in.
*/
static class Solution {
public int[] productExceptSelf(int[] nums){int n=nums.length;int[] a=new int[n];a[0]=1;
for(int i=1;i<n;i++)a[i]=a[i-1]*nums[i-1];int r=1;for(int i=n-1;i>=0;i--){a[i]*=r;r*=nums[i];}return a;}
public static void main(String[] args) {
System.out.println(Arrays.toString(new Solution().productExceptSelf(new int[]{1,2,3,4})));
}
}
public static void main(String[] args) {
// Lesson example: [24, 12, 8, 6]
Solution.main(args);
// A zero makes every other slot 0 and its own slot the product of the rest: [0, 6, 0, 0]
System.out.println(Arrays.toString(new Solution().productExceptSelf(new int[]{2, 0, 3, 1})));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[1,2,3,4]→[24,12,8,6]
Also asked: Amazon
D47. Container With Most Water
O(n)/O(1)Two pointersJava + JS lab
Max water area between two vertical lines.
Approach
Ends inward; advance the shorter side.
Easy Algorithm
L,R ends; track best.
Move shorter pointer.
Code (Java)
import java.util.*;
/**
* Demo wrapper for the course snippet. Solution is nested so the whole program fits
* in one file; the snippet's own main prints the lesson example.
*/
public class Main {
/**
* Two-pointer scan from both ends: always move the shorter wall inward, tracking the best area.
*/
static class Solution {
public int maxArea(int[] h){int L=0,R=h.length-1,b=0;while(L<R){b=Math.max(b,Math.min(h[L],h[R])*(R-L));if(h[L]<h[R])L++;else R--;}return b;}
public static void main(String[] args) {
System.out.println(new Solution().maxArea(new int[]{1,8,6,2,5,4,8,3,7}));
}
}
public static void main(String[] args) {
// Lesson example: walls of height 8 (index 1) and 7 (index 8) give 7 * 7 = 49
Solution.main(args);
// A second case: two walls of height 1, one apart, hold an area of 1
System.out.println(new Solution().maxArea(new int[]{1, 1}));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
classic heights → 49
Also asked: Amazon
D48. Longest Palindromic Substring
O(n²)/O(1)Expand around centersJava + JS lab
Longest palindromic substring (any if ties).
Approach
Expand odd/even centers; keep best window.
Easy Algorithm
For each i expand(i,i) and expand(i,i+1).
Track max window.
Code (Java)
import java.util.*;
/**
* Demo wrapper for the course snippet. Solution is nested so the whole program fits
* in one file; the snippet's own main prints the lesson example.
*/
public class Main {
/**
* Expand around each center (odd and even length) and keep the longest palindrome found.
*/
static class Solution {
public String longestPalindrome(String s){int st=0,en=0;for(int i=0;i<s.length();i++){int[] a=ex(s,i,i),b=ex(s,i,i+1);int[] c=a[1]-a[0]>=b[1]-b[0]?a:b;if(c[1]-c[0]>en-st){st=c[0];en=c[1];}}return s.substring(st,en+1);}
int[] ex(String s,int L,int R){while(L>=0&&R<s.length()&&s.charAt(L)==s.charAt(R)){L--;R++;}return new int[]{L+1,R-1};}
public static void main(String[] args) {
System.out.println(new Solution().longestPalindrome("babad"));
}
}
public static void main(String[] args) {
// Lesson example: "babad" -> "bab" ("aba" is an equally long valid answer)
Solution.main(args);
// Even-length palindrome: "cbbd" contains "bb"
System.out.println(new Solution().longestPalindrome("cbbd"));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
babad → bab
Also asked: Amazon, Microsoft
D49. Group Anagrams
O(n k log k)Hash by sorted keyJava + JS lab
Group strings that are anagrams.
Approach
Sorted-char key → bucket list.
Easy Algorithm
key=sorted(s)
map key→list; return values
Code (Java)
import java.util.*;
/**
* Demo wrapper for the course snippet. Solution is nested so the whole program fits
* in one file; the snippet's own main prints the lesson example.
*/
public class Main {
/**
* Groups anagrams by using each word's sorted letters as a hash map key.
*/
static class Solution {
public List<List<String>> groupAnagrams(String[] strs){Map<String,List<String>> m=new HashMap<>();
for(String s:strs){char[] c=s.toCharArray();Arrays.sort(c);m.computeIfAbsent(new String(c),k->new ArrayList<>()).add(s);}
return new ArrayList<>(m.values());}
public static void main(String[] args) {
System.out.println(new Solution().groupAnagrams(new String[]{"eat","tea","tan","ate","nat","bat"}));
}
}
public static void main(String[] args) {
// Lesson example: three groups (eat/tea/ate, tan/nat, bat); the order of groups follows the HashMap
Solution.main(args);
// A single word forms a single group
System.out.println(new Solution().groupAnagrams(new String[]{"a"}));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
eat/tea/ate together
Also asked: Amazon, Uber
D50. Valid Anagram
O(n)/O(1)Letter countsJava + JS lab
True if t is an anagram of s.
Approach
Count letters; all counts must return to zero.
Easy Algorithm
length check
inc s / dec t; verify zeros
Code (Java)
import java.util.*;
/**
* Demo wrapper for the course snippet. Solution is nested so the whole program fits
* in one file; the snippet's own main prints the lesson example.
*/
public class Main {
/**
* Compares letter counts: increment for s, decrement for t, and every count must end at zero.
*/
static class Solution {
public boolean isAnagram(String s,String t){if(s.length()!=t.length())return false;int[] c=new int[26];
for(int i=0;i<s.length();i++){c[s.charAt(i)-'a']++;c[t.charAt(i)-'a']--;}for(int x:c)if(x!=0)return false;return true;}
public static void main(String[] args) {
System.out.println(new Solution().isAnagram("anagram","nagaram"));
}
}
public static void main(String[] args) {
// Lesson example: "anagram" and "nagaram" use the same letters
Solution.main(args);
// Different letters: "rat" and "car" are not anagrams
System.out.println(new Solution().isAnagram("rat", "car"));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
anagram/nagaram → true
Also asked: Amazon, Target
D51. Move Zeroes
O(n)/O(1)Two-pointer write indexJava + JS lab
Move zeroes to end in-place; keep nonzero order. Return resulting array.
Approach
Compact nonzeros forward; fill rest with zeroes.
Easy Algorithm
w=0 write nonzeros
fill [w..] with 0
Code (Java)
import java.util.*;
/**
* Demo wrapper for the course snippet. Solution is nested so the whole program fits
* in one file; the snippet's own main prints the lesson example.
*/
public class Main {
/**
* Two-pointer, in place: copy every non-zero forward to the write index, then fill the rest with zeroes.
*/
static class Solution {
public int[] moveZeroes(int[] nums){int w=0;for(int x:nums)if(x!=0)nums[w++]=x;while(w<nums.length)nums[w++]=0;return nums;}
public static void main(String[] args) {
int[] a=new int[]{0,1,0,3,12}; System.out.println(Arrays.toString(new Solution().moveZeroes(a)));
}
}
public static void main(String[] args) {
// Lesson example: [0, 1, 0, 3, 12] -> [1, 3, 12, 0, 0]
Solution.main(args);
// Already compact: nothing to move
System.out.println(Arrays.toString(new Solution().moveZeroes(new int[]{1, 2, 3})));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[0,1,0,3,12]→[1,3,12,0,0]
Also asked: Amazon
D52. Best Time to Buy and Sell Stock II
O(n)/O(1)Greedy sum of risesJava + JS lab
Unlimited transactions; max profit.
Approach
Sum every positive adjacent price difference.
Easy Algorithm
for each ascent add delta
Code (Java)
import java.util.*;
// Main entry point for the program
public class Main {
// Solution class for Best Time to Buy and Sell Stock II problem
// Calculates maximum profit from unlimited buy/sell transactions
static class Solution {
// Original method from snippet - calculates max profit by summing all positive price differences
public int maxProfitII(int[] p){int a=0;for(int i=1;i<p.length;i++)if(p[i]>p[i-1])a+=p[i]-p[i-1];return a;}
// Original main method from snippet - tests with sample input
public static void main(String[] args) {
System.out.println(new Solution().maxProfitII(new int[]{7,1,5,3,6,4}));
}
}
// Main method to run the program and exercise the snippet
public static void main(String[] args) {
// Run the original test from Solution's main
Solution.main(args);
// Additional test cases to demonstrate the solution with various inputs
Solution sol = new Solution();
System.out.println("Additional tests:");
System.out.println("Test 2 (increasing prices): " + sol.maxProfitII(new int[]{1,2,3,4,5})); // Expected: 4
System.out.println("Test 3 (decreasing prices): " + sol.maxProfitII(new int[]{7,6,4,3,1})); // Expected: 0
System.out.println("Test 4 (single price): " + sol.maxProfitII(new int[]{1})); // Expected: 0
System.out.println("Test 5 (empty array): " + sol.maxProfitII(new int[]{})); // Expected: 0
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[7,1,5,3,6,4]→7
Also asked: Amazon
D53. Find Minimum in Rotated Sorted Array
O(log n)/O(1)Binary search pivotJava + JS lab
Min element in rotated sorted distinct array.
Approach
If mid > right, min is to the right; else mid or left.
Easy Algorithm
while lo<hi
branch on a[mid]>a[hi]
Code (Java)
import java.util.*;
public class Main {
// Solution class implements binary search to find the minimum in a rotated sorted array
static class Solution {
public int findMin(int[] a){int lo=0,hi=a.length-1;while(lo<hi){int m=(lo+hi)/2;if(a[m]>a[hi])lo=m+1;else hi=m;}return a[lo];} // binary search loop: narrow range until min found
public static void main(String[] args) { // demo main from the snippet
System.out.println(new Solution().findMin(new int[]{3,4,5,1,2})); // test case: rotated array, expected 1
}
}
public static void main(String[] args) {
Solution.main(args); // run the snippet's main
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[3,4,5,1,2]→1
Also asked: Amazon, Uber
D54. Search in Rotated Sorted Array
O(log n)/O(1)Rotated binary searchJava + JS lab
Index of target in rotated sorted array, or -1.
Approach
Identify sorted half; binary-search the half that can hold target.
Easy Algorithm
compare mid
shrink to feasible sorted half
Code (Java)
import java.util.*; // Import utilities
/**
* Main entry point for the rotated array search demo.
*/
public class Main {
/**
* Solution class containing the rotated sorted array search algorithm.
* Finds target index in a rotated sorted array using binary search O(log n).
*/
static class Solution {
public int searchRotated(int[] a,int t){int lo=0,hi=a.length-1;while(lo<=hi){int m=(lo+hi)/2;if(a[m]==t)return m; // Binary search: compute mid, check for target
if(a[lo]<=a[m]){if(a[lo]<=t&&t<a[m])hi=m-1;else lo=m+1;}else{if(a[m]<t&&t<=a[hi])lo=m+1;else hi=m-1;}}return -1;} // Identify sorted half and narrow search range
public static void main(String[] args) { // Demo main from original snippet
System.out.println(new Solution().searchRotated(new int[]{4,5,6,7,0,1,2},0)); // Search for 0 in rotated array, prints index 4
}
}
// Main entry point calls Solution.main to run the demo
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[4,5,6,7,0,1,2],0 → 4
Also asked: Amazon, Meta
D55. Combination Sum
exponentialBacktracking with reuseJava + JS lab
All combinations summing to target; candidates reusable.
Approach
DFS with start index; reuse same index to keep nondecreasing order.
Easy Algorithm
dfs(remain,start,path)
for i>=start push/recurse(i)/pop
Code (Java)
// Imports Java utility classes for collections.
import java.util.*;
// Outer main class that hosts the solution and program entry point.
public class Main {
// Static nested class containing the combination sum algorithm (candidates may be reused).
static class Solution {
// Public method to find all unique combinations that sum to target; initializes result list and starts DFS.
public List<List<Integer>> combinationSum(int[] c,int t){List<List<Integer>> res=new ArrayList<>();dfs(c,t,0,new ArrayList<>(),res);return res;}
// Private helper method performing depth-first search with backtracking to explore combinations.
void dfs(int[] c,int rem,int start,List<Integer> path,List<List<Integer>> res){if(rem==0){res.add(new ArrayList<>(path));return;}
// Loop through candidates starting from index; skip if candidate exceeds remaining target; add candidate, recurse, then backtrack.
for(int i=start;i<c.length;i++){if(c[i]>rem)continue;path.add(c[i]);dfs(c,rem-c[i],i,path,res);path.remove(path.size()-1);}}
// Main method inside Solution class for demonstration.
public static void main(String[] args) {
// Demo call: prints combinations of [2,3,6,7] that sum to 7.
System.out.println(new Solution().combinationSum(new int[]{2,3,6,7},7));
}
}
// Outer program entry point; delegates to Solution.main.
public static void main(String[] args) {
// Calls the demonstration main method of Solution class.
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[2,3,6,7],7 → [[2,2,3],[7]]
Also asked: Amazon
D56. Word Search
O(N·3^L)Grid backtrackingJava + JS lab
Does `word` exist in board via 4-adjacent cells without reuse?
Approach
DFS from each matching start; mark visited then restore.
Easy Algorithm
try each cell
dfs mark '#', explore 4 dirs, unmark
Code (Java)
import java.util.*; // Standard utilities import
public class Main {
// Nested Solution class containing the word search algorithm
static class Solution {
public boolean exist(char[][] b,String w){for(int r=0;r<b.length;r++)for(int c=0;c<b[0].length;c++)if(dfs(b,w,0,r,c))return true;return false;} // Checks if word exists in board using DFS backtracking
boolean dfs(char[][] b,String w,int i,int r,int c){if(i==w.length())return true;if(r<0||c<0||r>=b.length||c>=b[0].length||b[r][c]!=w.charAt(i))return false; // Depth-first search from (r,c) matching w starting at index i
char t=b[r][c];b[r][c]='#';boolean ok=dfs(b,w,i+1,r+1,c)||dfs(b,w,i+1,r-1,c)||dfs(b,w,i+1,r,c+1)||dfs(b,w,i+1,r,c-1);b[r][c]=t;return ok;} // Mark visited, explore 4 directions, restore
public static void main(String[] args) { // Original main method for demonstration
System.out.println(new Solution().exist(new char[][]{{'A','B','C','E'},{'S','F','C','S'},{'A','D','E','E'}}, "ABCCED"));
}
}
// Entry point: delegates to Solution.main
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
ABCCED on classic board → true
Also asked: Amazon, Microsoft
D57. House Robber
O(n)/O(1)1D DPJava + JS lab
Max money robbing houses; cannot rob adjacent houses.
Approach
dp[i]=max(dp[i-1], dp[i-2]+nums[i]); roll two variables.
Easy Algorithm
prev2,prev1
for x: cur=max(prev1, prev2+x); shift
Code (Java)
import java.util.*;
/**
* Main class to run the House Robber solution.
* The Solution class is nested statically as required.
*/
public class Main {
/**
* Solution class for the House Robber problem.
* Computes maximum money that can be robbed without robbing adjacent houses.
* Uses O(1) space DP with two variables.
*/
static class Solution {
// Original line preserved exactly as given
public int rob(int[] a){int p2=0,p1=0;for(int x:a){int cur=Math.max(p1,p2+x);p2=p1;p1=cur;}return p1;}
// Original main method preserved exactly
public static void main(String[] args) {
System.out.println(new Solution().rob(new int[]{1,2,3,1}));
}
}
/**
* Main entry point - delegates to Solution.main to exercise the snippet.
*/
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[1,2,3,1]→4 (1+3)
Also asked: Amazon, Airbnb
D58. House Robber II
O(n)/O(1)Circular DPJava + JS lab
Houses in a circle; first and last adjacent. Max rob.
Approach
Max of rob linear range [0..n-2] and [1..n-1].
Easy Algorithm
if n==1 return a[0]
return max(robLine(0,n-2), robLine(1,n-1))
Code (Java)
import java.util.*;
// Main entry point for the House Robber II demonstration
public class Main {
// Solution class implementing the circular House Robber algorithm
static class Solution {
// Robs maximum amount from circular arrangement of houses
public int rob2(int[] a){int n=a.length;if(n==1)return a[0];return Math.max(line(a,0,n-2),line(a,1,n-1));} // Computes max of two linear subproblems excluding first/last house
// Helper for linear house robber using O(1) space DP
int line(int[] a,int L,int R){int p2=0,p1=0;for(int i=L;i<=R;i++){int cur=Math.max(p1,p2+a[i]);p2=p1;p1=cur;}return p1;} // Iterates range [L,R], maintains two previous max values
// Tests the solution with sample input [2,3,2] expected output 3
public static void main(String[] args) {
System.out.println(new Solution().rob2(new int[]{2,3,2}));
}
}
// Program entry point delegates to Solution.main
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[2,3,2]→3
Also asked: Amazon
D59. Climbing Stairs
O(n)/O(1)Fibonacci DPJava + JS lab
Ways to climb n stairs taking 1 or 2 steps.
Approach
ways(n)=ways(n-1)+ways(n-2); roll two ints.
Easy Algorithm
a=1,b=1
repeat n-1 times: a,b = b, a+b
Code (Java)
import java.util.*;
public class Main {
// Nested Solution class containing the climbing stairs algorithm
static class Solution {
public int climbStairs(int n){int a=1,b=1;for(int i=0;i<n-1;i++){int c=a+b;a=b;b=c;}return b;} // Fibonacci-style DP: compute ways to climb n stairs
public static void main(String[] args) { // Demo entry point in Solution
System.out.println(new Solution().climbStairs(3)); // Prints 3 for n=3
}
}
// Main entry point: delegates to Solution.main
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
n=3 → 3
Also asked: Amazon
D60. Unique Paths
O(mn)/O(n)Grid DPJava + JS lab
Robot paths from top-left to bottom-right moving only right/down.
Approach
dp[r][c]=dp[r-1][c]+dp[r][c-1]; use 1D rolling row.
Easy Algorithm
row of 1s length n
for each of m-1 rows: prefix-sum update
Code (Java)
import java.util.*; // Import utilities like Arrays
public class Main {
// Solution class implementing unique paths calculation using 1D DP
static class Solution {
// Calculate unique paths from top-left to bottom-right in m x n grid
public int uniquePaths(int m,int n){int[] dp=new int[n];Arrays.fill(dp,1);
for(int r=1;r<m;r++)for(int c=1;c<n;c++)dp[c]+=dp[c-1];return dp[n-1];}
public static void main(String[] args) {
System.out.println(new Solution().uniquePaths(3,7));
}
}
// Entry point: delegate to Solution.main
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Length of longest strictly increasing subsequence.
Approach
Maintain tails of active LIS piles; binary search replace.
Easy Algorithm
tails=[]; for x: lower_bound replace or append
answer=tails.size
Code (Java)
import java.util.*; // Standard utilities for List, ArrayList, Collections
public class Main {
// Nested Solution class containing the O(n log n) LIS algorithm using patience sorting with binary search
static class Solution {
// Original method: computes length of longest strictly increasing subsequence
public int lengthOfLIS(int[] nums){List<Integer> t=new ArrayList<>(); // tails of active piles
for(int x:nums){int i=Collections.binarySearch(t,x);if(i<0)i=-(i+1);if(i==t.size())t.add(x);else t.set(i,x);}return t.size();}
// Original main method demonstrating usage with sample input
public static void main(String[] args) {
System.out.println(new Solution().lengthOfLIS(new int[]{10,9,2,5,3,7,101,18})); // prints 4
}
}
// Main entry point delegates to Solution.main to run the demo
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[10,9,2,5,3,7,101,18]→4
Also asked: Amazon, Google
D62. Edit Distance
O(mn)/O(mn)2D DP LevenshteinJava + JS lab
Min insert/delete/replace to convert word1→word2.
Approach
dp[i][j] from prefixes; take min of insert/delete/replace.
Easy Algorithm
init borders as i/j
if equal diag else 1+min(3 ops)
Code (Java)
import java.util.*;
// Main class to run the edit distance solution
public class Main {
// Solution class implementing Levenshtein edit distance
static class Solution {
public int minDistance(String a,String b){int m=a.length(),n=b.length();int[][] dp=new int[m+1][n+1]; // compute lengths and allocate DP table
for(int i=0;i<=m;i++)dp[i][0]=i;for(int j=0;j<=n;j++)dp[0][j]=j; // initialize borders: cost of deleting/inserting all chars
for(int i=1;i<=m;i++)for(int j=1;j<=n;j++){ // fill DP for each prefix pair
if(a.charAt(i-1)==b.charAt(j-1))dp[i][j]=dp[i-1][j-1]; // chars match: no extra cost, take diagonal
else dp[i][j]=1+Math.min(dp[i-1][j-1],Math.min(dp[i][j-1],dp[i-1][j]));} // mismatch: 1 + min of replace, insert, delete
return dp[m][n];} // final answer in bottom-right cell
public static void main(String[] args) { // demo: edit distance between "horse" and "ros"
System.out.println(new Solution().minDistance("horse","ros"));
}
}
// Entry point: delegates to Solution.main for demonstration
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
horse→ros = 3
Also asked: Amazon, Google
D63. Maximum Subarray (Kadane)
O(n)/O(1)Kadane DPJava + JS lab
Largest sum contiguous subarray.
Approach
Track best ending here; reset when negative contribution.
Easy Algorithm
best=cur=nums[0]
cur=max(x, cur+x); best=max(best,cur)
Code (Java)
import java.util.*;
// Main class to run the program
public class Main {
// Solution class containing Kadane's algorithm for maximum subarray sum
static class Solution {
public int maxSubArray(int[] a){int best=a[0],cur=a[0];for(int i=1;i<a.length;i++){cur=Math.max(a[i],cur+a[i]);best=Math.max(best,cur);}return best;}
public static void main(String[] args) {
System.out.println(new Solution().maxSubArray(new int[]{-2,1,-3,4,-1,2,1,-5,4}));
}
}
// Main entry point that delegates to Solution.main
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[-2,1,-3,4,-1,2,1,-5,4]→6
Also asked: Amazon, Target
D64. Insert Interval
O(n)/O(n)Interval merge sweepJava + JS lab
Insert newInterval into sorted non-overlapping intervals; merge.
Approach
Add all before; merge overlaps; append rest.
Easy Algorithm
push intervals ending before new start
merge while overlapping
append remaining
Code (Java)
import java.util.*;
// Main class to run the interval insertion program
public class Main {
// Solution class containing the algorithm to insert and merge intervals
static class Solution {
// Inserts a new interval into sorted non-overlapping intervals and merges overlaps
public int[][] insertInterval(int[][] intervals,int[] neu){List<int[]> res=new ArrayList<>();int i=0,n=intervals.length; // Initialize result list and loop variables
while(i<n&&intervals[i][1]<neu[0])res.add(intervals[i++]); // Add all intervals ending before new interval starts
while(i<n&&intervals[i][0]<=neu[1]){neu[0]=Math.min(neu[0],intervals[i][0]);neu[1]=Math.max(neu[1],intervals[i][1]);i++;} // Merge overlapping intervals with new interval
res.add(neu);while(i<n)res.add(intervals[i++]);return res.toArray(new int[0][]);} // Add merged interval and append remaining intervals
// Test the insertInterval method with a sample case
public static void main(String[] args) {
// Expected output: [[1,5],[6,9]] - [2,5] merges with [1,3]
System.out.println(Arrays.deepToString(new Solution().insertInterval(new int[][]{{1,3},{6,9}}, new int[]{2,5})));
}
}
// Program entry point delegates to Solution.main
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[[1,3],[6,9]] + [2,5] → [[1,5],[6,9]]
Also asked: Amazon, Google
D65. Non-overlapping Intervals
O(n log n)/O(1)Greedy earliest endJava + JS lab
Min removals so remaining intervals are non-overlapping.
Approach
Sort by end; keep interval if start >= lastEnd else remove (count++).
Easy Algorithm
sort by end
greedy take non-overlapping; count skips
Code (Java)
import java.util.*;
/**
* Main entry point for the interval overlap removal demo.
* Nests the Solution class from the course snippet and delegates to its main.
*/
public class Main {
/**
* Solution class containing the greedy algorithm to find minimum
* number of intervals to remove so the rest are non-overlapping.
* Sorts intervals by end time, then counts overlaps.
*/
static class Solution {
// Original method: returns minimum removals for non-overlapping intervals
public int eraseOverlapIntervals(int[][] a){Arrays.sort(a,Comparator.comparingInt(x->x[1]));int end=a[0][1],rm=0;
for(int i=1;i<a.length;i++){if(a[i][0]<end)rm++;else end=a[i][1];}return rm;}
// Original main: tests with sample intervals {{1,2},{2,3},{3,4},{1,3}}
public static void main(String[] args) {
System.out.println(new Solution().eraseOverlapIntervals(new int[][]{{1,2},{2,3},{3,4},{1,3}}));
}
}
/**
* Program entry point: delegates to Solution.main to run the demo.
*/
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[[1,2],[2,3],[3,4],[1,3]] → remove 1
Also asked: Amazon
D66. Min Stack
O(1)/O(n)Aux stack of minimaJava + JS lab
Stack with push/pop/top/getMin in O(1).
Approach
Parallel min stack: push min(x, curMin) with each value.
Easy Algorithm
ops runner: MinStack commands return outputs
Code (Java)
import java.util.*;
/**
* Main entry point for the MinStack demonstration.
* Delegates to Solution.main to execute the example.
*/
public class Main {
/**
* Solution class containing the MinStack implementation and a runner
* that processes a list of operations (commands) against it.
*/
static class Solution {
/**
* MinStack implementation using two deques:
* - st stores the pushed values
* - mn stores the running minimum at each depth
* All operations run in O(1) time.
*/
static class MinStack{Deque<Integer> st=new ArrayDeque<>(), mn=new ArrayDeque<>(); // two stacks: values and running minima
void push(int x){st.push(x);mn.push(mn.isEmpty()?x:Math.min(mn.peek(),x));} // push value and new min
void pop(){st.pop();mn.pop();} int top(){return st.peek();} int getMin(){return mn.peek();}} // pop both; top returns last value; getMin returns current minimum
/**
* Executes a sequence of MinStack operations.
* @param ops list of operations, each a list where first element is method name
* @return list of results (null for void ops, value for top/getMin)
*/
public List<Object> runMinStack(List<List<Object>> ops){MinStack s=null;List<Object> out=new ArrayList<>();
for(List<Object> op:ops){String m=(String)op.get(0);if(m.equals("MinStack")){s=new MinStack();out.add(null);}
else if(m.equals("push")){s.push(((Number)op.get(1)).intValue());out.add(null);}
else if(m.equals("pop")){s.pop();out.add(null);} else if(m.equals("top"))out.add(s.top()); else out.add(s.getMin());}
return out;}
public static void main(String[] args) {
System.out.println(new Solution().runMinStack(List.of(List.of("MinStack"),List.of("push",-2),List.of("push",0),List.of("push",-3),List.of("getMin"),List.of("pop"),List.of("top"),List.of("getMin"))));
}
}
/**
* Program entry point: calls Solution.main to run the built-in example.
*/
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
push -2,-3; getMin→-3
Also asked: Amazon, Bloomberg
D67. Next Greater Element II
O(n)/O(n)Monotonic stack circularJava + JS lab
Next greater for each element in circular array (-1 if none).
Approach
Traverse 2n with decreasing stack of indices; fill when larger seen.
Easy Algorithm
ans fill -1; stack indices
for i in 0..2n-1: while stack top < nums[i%n] set ans
Code (Java)
// Import utility classes for collections and arrays
import java.util.*;
// Main class containing the solution and program entry point
public class Main {
// Solution class containing the next greater element algorithm for circular arrays
// Static nested class implementing the next greater element algorithm
static class Solution {
// Method to find next greater elements in circular array using monotonic stack
public int[] nextGreaterElements(int[] a){int n=a.length;int[] ans=new int[n];Arrays.fill(ans,-1);Deque<Integer> st=new ArrayDeque<>();
// Iterate twice over array to simulate circular behavior, maintain decreasing stack
for(int i=0;i<2*n;i++){int x=a[i%n];while(!st.isEmpty()&&a[st.peek()]<x)ans[st.pop()]=x;if(i<n)st.push(i);}return ans;}
// Main method in Solution class for demonstration
public static void main(String[] args) {
// Test with sample input [1,2,1] and print result
System.out.println(Arrays.toString(new Solution().nextGreaterElements(new int[]{1,2,1})));
}
// End of Solution class
}
// Entry point: delegates to Solution.main to run the demo
// Program entry point that delegates to Solution.main
public static void main(String[] args) {
// Call Solution's main method to run demo
Solution.main(args);
}
// End of Main class
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[1,2,1]→[2,-1,2]
Also asked: Amazon
D68. Asteroid Collision
O(n)/O(n)Stack simulationJava + JS lab
Asteroids on a line; positive→right, negative←left; same-size both explode.
Approach
Stack of survivors; resolve collisions when right-moving meets left-moving.
Easy Algorithm
for each a: while collision possible resolve
push if survives
Code (Java)
import java.util.*;
// Main entry point for the program
public class Main {
// Solution class containing the asteroid collision algorithm
static class Solution {
// Asteroid collision method - resolves collisions between asteroids moving in opposite directions
public int[] asteroidCollision(int[] a){Deque<Integer> st=new ArrayDeque<>();
for(int x:a){boolean live=true;while(live&&!st.isEmpty()&&st.peek()>0&&x<0){int y=st.peek();if(y<-x)st.pop();else{if(y==-x)st.pop();live=false;}}
if(live)st.push(x);}int[] ans=new int[st.size()];for(int i=ans.length-1;i>=0;i--)ans[i]=st.pop();return ans;}
// Main method in Solution class for testing
public static void main(String[] args) {
System.out.println(Arrays.toString(new Solution().asteroidCollision(new int[]{5,10,-5})));
}
}
// Main method that delegates to Solution.main
public static void main(String[] args){ Solution.main(args); }
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[5,10,-5]→[5,10]
Also asked: Amazon, Lyft
D69. Binary Tree Level Order Traversal
O(n)/O(n)BFS queueJava + JS lab
Level-order values of binary tree (array form input).
Approach
BFS; for each level size, drain that many nodes.
Easy Algorithm
queue root
while queue: take level size, collect vals, enqueue children
Code (Java)
// Import Java utility classes like List, Queue, ArrayDeque, and ArrayList.
import java.util.*;
// Main entry class containing the Solution and a delegating main method.
public class Main {
// Solution class containing the level order traversal algorithm and TreeNode definition
// Static inner class Solution encapsulating the level-order traversal logic and tree utilities.
static class Solution {
// In guide: use TreeNode; HTML lab converts arrays.
// levelOrder performs breadth-first traversal, returning a list of levels each as a list of node values.
public List<List<Integer>> levelOrder(TreeNode root){List<List<Integer>> res=new ArrayList<>();if(root==null)return res;
// Initialize a queue with the root node to start level-order processing.
Queue<TreeNode> q=new ArrayDeque<>();q.add(root);
// While queue has nodes, capture current level size, then iterate that many nodes to build the level list.
while(!q.isEmpty()){int sz=q.size();List<Integer> lvl=new ArrayList<>();
// For each node in the current level, record its value and enqueue its non-null children.
for(int i=0;i<sz;i++){TreeNode n=q.poll();lvl.add(n.val);if(n.left!=null)q.add(n.left);if(n.right!=null)q.add(n.right);}res.add(lvl);}
// Return the accumulated list of levels after the queue is exhausted.
return res;}
// TreeNode defines a binary tree node with an integer value and left/right child references.
static class TreeNode{int val;TreeNode left,right;TreeNode(int v){val=v;}}
// Helper to build a binary tree from a level-order array (with nulls for missing nodes)
// arrToTree builds a binary tree from a level-order array where null represents missing nodes.
static TreeNode arrToTree(Integer[] arr) {
// Return null for empty or null input arrays, or when the first element is null.
if (arr == null || arr.length == 0 || arr[0] == null) return null;
// Create the root node from the first array element.
TreeNode root = new TreeNode(arr[0]);
// Queue used to attach children to nodes in level order.
Queue<TreeNode> queue = new ArrayDeque<>();
// Seed the queue with the root node.
queue.add(root);
// Index i tracks the next array element to consume as a child.
int i = 1;
// Process nodes until the queue is empty or all array elements are consumed.
while (!queue.isEmpty() && i < arr.length) {
// Dequeue the next parent node to attach its children.
TreeNode current = queue.poll();
// Left child
// If a left child value exists, create the node, link it, and enqueue it.
if (i < arr.length && arr[i] != null) {
current.left = new TreeNode(arr[i]);
queue.add(current.left);
}
// Advance index to the potential right child position.
i++;
// Right child
// If a right child value exists, create the node, link it, and enqueue it.
if (i < arr.length && arr[i] != null) {
current.right = new TreeNode(arr[i]);
queue.add(current.right);
}
// Advance index past the right child for the next loop iteration.
i++;
}
// Return the fully constructed tree root.
return root;
}
// Solution's main demonstrates levelOrder on a sample tree [3,9,20,null,null,15,7].
public static void main(String[] args) {
// Build the tree from the array and print the level-order traversal result.
System.out.println(new Solution().levelOrder(arrToTree(new Integer[]{3,9,20,null,null,15,7}))); /* assume helpers */
}
}
// Outer Main.main delegates to Solution.main for execution.
public static void main(String[] args) {
// Invoke the demonstration in Solution.
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[3,9,20,null,null,15,7] → [[3],[9,20],[15,7]]
Also asked: Amazon, Meta
D70. Validate Binary Search Tree
O(n)/O(h)Bounds DFSJava + JS lab
Is binary tree a valid BST?
Approach
DFS with (low, high) open bounds on allowed values.
Easy Algorithm
dfs(node,lo,hi)
val must be in (lo,hi); recurse children
Code (Java)
import java.util.*;
// Original import from snippet
public class Main {
// Snippet's Solution class made static nested
static class Solution {
// Validates BST using DFS with open bounds (lo, hi)
public boolean isValidBST(TreeNode root){return dfs(root,Long.MIN_VALUE,Long.MAX_VALUE);}
// Recursive DFS: node value must be strictly between lo and hi
boolean dfs(TreeNode n,long lo,long hi){if(n==null)return true;if(n.val<=lo||n.val>=hi)return false;return dfs(n.left,lo,n.val)&&dfs(n.right,n.val,hi);}
// TreeNode definition from snippet
static class TreeNode{int val;TreeNode left,right;TreeNode(int v){val=v;}}
// Snippet's main: tests with null tree (should print true)
public static void main(String[] args) {
System.out.println(new Solution().isValidBST(/*tree*/null));
}
}
// Main entry point: delegates to Solution.main
public static void main(String[] args) {
Solution.main(args); // the snippet's own demo: an empty tree is a valid BST
Solution sol = new Solution();
// Valid BST: 2 (1 < 2 < 3)
// / \
// 1 3
Solution.TreeNode ok = new Solution.TreeNode(2);
ok.left = new Solution.TreeNode(1);
ok.right = new Solution.TreeNode(3);
System.out.println(sol.isValidBST(ok)); // true
// Invalid: 4 is in the right subtree of 5 but is smaller than 5 (3 breaks the same bound)
// 5
// / \
// 1 4
// / \
// 3 6
Solution.TreeNode bad = new Solution.TreeNode(5);
bad.left = new Solution.TreeNode(1);
bad.right = new Solution.TreeNode(4);
bad.right.left = new Solution.TreeNode(3);
bad.right.right = new Solution.TreeNode(6);
System.out.println(sol.isValidBST(bad)); // false
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[2,1,3] true; [5,1,4,null,null,3,6] false
Also asked: Amazon, Meta
D71. Diameter of Binary Tree
O(n)/O(h)DFS height + best pathJava + JS lab
Length (edges) of longest path between any two nodes.
Approach
For each node, diameter through node = leftH+rightH; track global max.
Easy Algorithm
dfs returns height
update best = max(best, L+R)
Code (Java)
import java.util.*; // import all utilities
// Main class to run the program
public class Main {
// Solution class containing binary tree diameter algorithm
static class Solution {
// Field to track maximum diameter
int best=0;public int diameterOfBinaryTree(TreeNode root){best=0;height(root);return best;} // computes diameter of binary tree
// Helper method to compute height and update best diameter
int height(TreeNode n){if(n==null)return 0;int L=height(n.left),R=height(n.right);best=Math.max(best,L+R);return 1+Math.max(L,R);} // returns height of node
// TreeNode definition for binary tree nodes
static class TreeNode{int val;TreeNode left,right;TreeNode(int v){val=v;}} // simple binary tree node
// Main method in Solution for testing with null tree
public static void main(String[] args) {
System.out.println(new Solution().diameterOfBinaryTree(null));
}
}
// Main entry point
public static void main(String[] args) {
Solution.main(args); // the snippet's own demo: an empty tree has diameter 0
// 1
// / \
// 2 3
// / \
// 4 5
Solution.TreeNode root = new Solution.TreeNode(1);
root.left = new Solution.TreeNode(2);
root.right = new Solution.TreeNode(3);
root.left.left = new Solution.TreeNode(4);
root.left.right = new Solution.TreeNode(5);
// Longest path is 4 -> 2 -> 1 -> 3 (or 5 -> 2 -> 1 -> 3): 3 edges
System.out.println(new Solution().diameterOfBinaryTree(root)); // 3
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[1,2,3,4,5] → 3
Also asked: Amazon
D72. Path Sum II
O(n²)/O(h)DFS backtracking pathsJava + JS lab
All root-to-leaf paths summing to targetSum.
Approach
DFS accumulate path; when leaf and sum matches, copy path.
Easy Algorithm
dfs(node,remain,path)
leaf & remain==val → record
Code (Java)
import java.util.*;
// Main class for JDoodle online compiler
public class Main {
// Solution class for Path Sum II problem - finds all root-to-leaf paths summing to targetSum
static class Solution {
// Returns all root-to-leaf paths where path sum equals target t
public List<List<Integer>> pathSum(TreeNode root,int t){List<List<Integer>> res=new ArrayList<>();dfs(root,t,new ArrayList<>(),res);return res;}
// DFS helper: accumulates path, checks leaf with remaining sum, backtracks
void dfs(TreeNode n,int rem,List<Integer> path,List<List<Integer>> res){if(n==null)return;path.add(n.val);
if(n.left==null&&n.right==null&&rem==n.val)res.add(new ArrayList<>(path));
else{dfs(n.left,rem-n.val,path,res);dfs(n.right,rem-n.val,path,res);}path.remove(path.size()-1);}
// Binary tree node definition
static class TreeNode{int val;TreeNode left,right;TreeNode(int v){val=v;}}
// Demonstrates pathSum with null tree (returns empty list)
public static void main(String[] args) {
System.out.println(new Solution().pathSum(null,22));
}
}
// Program entry point delegates to Solution.main for demo
public static void main(String[] args) {
Solution.main(args); // the snippet's own demo: an empty tree has no paths
// 5
// / \
// 4 8
// / / \
// 11 13 4
// / \ / \
// 7 2 5 1
Solution.TreeNode root = new Solution.TreeNode(5);
root.left = new Solution.TreeNode(4);
root.right = new Solution.TreeNode(8);
root.left.left = new Solution.TreeNode(11);
root.left.left.left = new Solution.TreeNode(7);
root.left.left.right = new Solution.TreeNode(2);
root.right.left = new Solution.TreeNode(13);
root.right.right = new Solution.TreeNode(4);
root.right.right.left = new Solution.TreeNode(5);
root.right.right.right = new Solution.TreeNode(1);
// Two root-to-leaf paths sum to 22: 5+4+11+2 and 5+8+4+5
System.out.println(new Solution().pathSum(root, 22)); // [[5, 4, 11, 2], [5, 8, 4, 5]]
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
classic tree target 22 → two paths
Also asked: Amazon
D73. Flatten Binary Tree to Linked List
O(n)/O(h)Morris / reverse postorderJava + JS lab
Flatten tree to right-linked list in preorder (in-place). Return array form of right spine.
Approach
Reverse preorder: process right, left, then splice node to prev.
Easy Algorithm
dfs(node): flatten right then left; node.right=prev; node.left=null; prev=node
Code (Java)
import java.util.*;
public class Main {
// Solution class containing the tree flattening algorithm
static class Solution {
TreeNode prev=null;public void flatten(TreeNode root){if(root==null)return;flatten(root.right);flatten(root.left);root.right=prev;root.left=null;prev=root;}
public TreeNode flattenTree(TreeNode root){prev=null;flatten(root);return root;}
static class TreeNode{int val;TreeNode left,right;TreeNode(int v){val=v;}}
public static void main(String[] args) {
/* flatten then serialize right chain */
}
}
// Main entry point: runs the original main and demonstrates the algorithm
public static void main(String[] args) {
// Call the original main (currently empty)
Solution.main(args);
// Build the sample tree: 1,2,5,3,4,null,6
// 1
// / \
// 2 5
// / \ \
// 3 4 6
Solution.TreeNode root = new Solution.TreeNode(1);
root.left = new Solution.TreeNode(2);
root.right = new Solution.TreeNode(5);
root.left.left = new Solution.TreeNode(3);
root.left.right = new Solution.TreeNode(4);
root.right.right = new Solution.TreeNode(6);
// Flatten the tree to a right-linked list (preorder)
Solution sol = new Solution();
sol.flattenTree(root);
// Print the right-spine values
System.out.print("Flattened right spine: ");
Solution.TreeNode curr = root;
while (curr != null) {
System.out.print(curr.val + " ");
curr = curr.right;
}
System.out.println();
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Return a valid course order (prereqs) or empty if cycle.
Approach
Build graph+indegree; BFS zero-indegree; if count<n cycle.
Easy Algorithm
Kahn BFS
if taken<n return [] else order
Code (Java)
import java.util.*;
// Main class to run the course schedule solution
public class Main {
// Solution class implementing Kahn's topological sort algorithm for course scheduling
static class Solution {
// Method to find a valid course order given prerequisites, returns empty array if cycle detected
public int[] findOrder(int n,int[][] pre){List<List<Integer>> g=new ArrayList<>();int[] indeg=new int[n]; // Create adjacency list and indegree array
for(int i=0;i<n;i++)g.add(new ArrayList<>());for(int[] e:pre){g.get(e[1]).add(e[0]);indeg[e[0]]++;} // Initialize graph and build edges with indegree counts
Queue<Integer> q=new ArrayDeque<>();for(int i=0;i<n;i++)if(indeg[i]==0)q.add(i); // Queue courses with zero prerequisites
int[] ord=new int[n];int k=0;while(!q.isEmpty()){int u=q.poll();ord[k++]=u;for(int v:g.get(u))if(--indeg[v]==0)q.add(v);} // Process courses in topological order
return k==n?ord:new int[0];} // Return order if all courses taken, else empty array
// Main method to test the findOrder function with sample input
public static void main(String[] args) {
System.out.println(Arrays.toString(new Solution().findOrder(2,new int[][]{{1,0}}))); // Test case: 2 courses, 1 requires 0
}
}
// Entry point that delegates to Solution's main method
public static void main(String[] args) {
Solution.main(args); // Call Solution's main to execute the test
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
2 courses [[1,0]] → [0,1]
Also asked: Amazon, Uber
D75. Network Delay Time
O(E log V)DijkstraJava + JS lab
Min time for signal from k to reach all n nodes; -1 if impossible.
Approach
Dijkstra on adjacency list; answer = max finite dist.
Easy Algorithm
build graph
Dijkstra from k
max dist or -1
Code (Java)
import java.util.*;
public class Main {
// Solution class implementing Dijkstra's algorithm for network delay time
static class Solution {
// Original method: computes minimum time for signal to reach all nodes from k
public int networkDelayTime(int[][] times,int n,int k){List<int[]>[] g=new List[n+1];for(int i=1;i<=n;i++)g[i]=new ArrayList<>();
for(int[] e:times)g[e[0]].add(new int[]{e[1],e[2]});int[] dist=new int[n+1];Arrays.fill(dist,Integer.MAX_VALUE);dist[k]=0;
PriorityQueue<int[]> pq=new PriorityQueue<>(Comparator.comparingInt(a->a[0]));pq.add(new int[]{0,k});
while(!pq.isEmpty()){int[] cur=pq.poll();int d=cur[0],u=cur[1];if(d>dist[u])continue;for(int[] e:g[u]){int v=e[0],w=e[1];if(d+w<dist[v]){dist[v]=d+w;pq.add(new int[]{dist[v],v});}}}
int ans=0;for(int i=1;i<=n;i++){if(dist[i]==Integer.MAX_VALUE)return -1;ans=Math.max(ans,dist[i]);}return ans;}
// Original main method that tests the solution with sample input
public static void main(String[] args) {
System.out.println(new Solution().networkDelayTime(new int[][]{{2,1,1},{2,3,1},{3,4,1}},4,2));
}
}
// Entry point that delegates to Solution.main
public static void main(String[] args){ Solution.main(args); }
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
n=4 times=[[2,1,1],[2,3,1],[3,4,1]] k=2 → 2
Also asked: Amazon, Uber
D76. Pacific Atlantic Water Flow
O(mn)Multi-source DFS/BFSJava + JS lab
Cells that can flow to both Pacific and Atlantic (edges).
Approach
DFS inland from ocean borders; intersection of reachable sets.
Easy Algorithm
mark pacific-reachable from top/left
atlantic from bottom/right
intersect
Code (Java)
import java.util.*;
// Main class to run the Pacific Atlantic Water Flow solution
public class Main {
// Solution class containing the algorithm to find cells that can flow to both oceans
static class Solution {
// Finds all coordinates from which water can flow to both Pacific and Atlantic oceans
public List<List<Integer>> pacificAtlantic(int[][] h){int R=h.length,C=h[0].length;boolean[][] P=new boolean[R][C],A=new boolean[R][C];
// Start DFS from Pacific borders (top and left)
for(int r=0;r<R;r++){dfs(h,P,r,0,0);dfs(h,A,r,C-1,0);}for(int c=0;c<C;c++){dfs(h,P,0,c,0);dfs(h,A,R-1,c,0);}
// Collect cells reachable from both oceans
List<List<Integer>> res=new ArrayList<>();for(int r=0;r<R;r++)for(int c=0;c<C;c++)if(P[r][c]&&A[r][c])res.add(List.of(r,c));return res;}
// Depth-first search to mark reachable cells flowing uphill from ocean edges
void dfs(int[][] h,boolean[][] seen,int r,int c,int prev){if(r<0||c<0||r>=h.length||c>=h[0].length||seen[r][c]||h[r][c]<prev)return;
seen[r][c]=true;dfs(h,seen,r+1,c,h[r][c]);dfs(h,seen,r-1,c,h[r][c]);dfs(h,seen,r,c+1,h[r][c]);dfs(h,seen,r,c-1,h[r][c]);}
// Demo main method that runs the algorithm on a sample height matrix
public static void main(String[] args) {
System.out.println(new Solution().pacificAtlantic(new int[][]{{1,2,2,3,5},{3,2,3,4,4},{2,4,5,3,1},{6,7,1,4,5},{5,1,1,2,4}}));
}
}
// Entry point: delegates to Solution's main method
public static void main(String[] args){ Solution.main(args); }
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
classic 5x5 heights → 7 cells
Also asked: Amazon, Google
D77. Number of Connected Components
O(n+e α)Union-FindJava + JS lab
n nodes 0..n-1 and undirected edges; count components.
Approach
Union-Find; start with n components; decrement on successful union.
Easy Algorithm
UF init n
union each edge
return components
Code (Java)
import java.util.*;
// Main class containing the Solution implementation for counting connected components
public class Main {
// Solution class implementing Union-Find to count connected components in an undirected graph
static class Solution {
// countComponents method: returns number of connected components using Union-Find with path compression
public int countComponents(int n,int[][] edges){int[] p=new int[n];for(int i=0;i<n;i++)p[i]=i;int comp=n; // initialize parent array and component count
java.util.function.IntUnaryOperator find=new java.util.function.IntUnaryOperator(){public int applyAsInt(int x){return p[x]==x?x:(p[x]=applyAsInt(p[x]));}}; // find with path compression
for(int[] e:edges){int a=find.applyAsInt(e[0]),b=find.applyAsInt(e[1]);if(a!=b){p[b]=a;comp--;}}return comp;} // union edges and count components
// Main method in Solution class for demonstration
public static void main(String[] args) {
System.out.println(new Solution().countComponents(5,new int[][]{{0,1},{1,2},{3,4}})); // test with 5 nodes and 3 edges
}
}
// Main entry point that delegates to Solution.main
public static void main(String[] args){ Solution.main(args); }
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Derive alien letter order from sorted words; return '' if invalid. Prefer smallest lexicographic among valid.
Approach
Build inequality edges from adjacent words; Kahn with min-heap for lex-smallest.
Easy Algorithm
compare adjacent words for first diff edge
detect prefix invalid
topo with PQ
Code (Java)
import java.util.*;
public class Main {
// Solution class for Alien Dictionary problem
// Derives lexicographically smallest valid character order from sorted dictionary words
static class Solution {
public String alienOrder(String[] words){Map<Character,Set<Character>> g=new HashMap<>();int[] indeg=new int[26];boolean[] seen=new boolean[26]; // Build graph, indegree, and track seen chars
for(String w:words)for(char c:w.toCharArray())seen[c-'a']=true; // Mark all characters that appear
for(int i=0;i+1<words.length;i++){String a=words[i],b=words[i+1];if(a.length()>b.length()&&a.startsWith(b))return ""; // Invalid prefix case
int len=Math.min(a.length(),b.length());for(int j=0;j<len;j++)if(a.charAt(j)!=b.charAt(j)){ // Find first differing char
char u=a.charAt(j),v=b.charAt(j);g.computeIfAbsent(u,k->new HashSet<>());if(g.get(u).add(v))indeg[v-'a']++;break;}} // Add edge u->v if new
PriorityQueue<Character> q=new PriorityQueue<>();for(char c='a';c<='z';c++)if(seen[c-'a']&&indeg[c-'a']==0)q.add(c); // Min-heap for lexicographically smallest topo order
StringBuilder sb=new StringBuilder();int need=0;for(boolean s:seen)if(s)need++; // Count unique chars needed
while(!q.isEmpty()){char u=q.poll();sb.append(u);for(char v:g.getOrDefault(u,Set.of()))if(--indeg[v-'a']==0)q.add(v);} // Kahn's algorithm with PQ
return sb.length()==need?sb.toString():"";} // Return order if all chars processed, else cycle detected
public static void main(String[] args) {
System.out.println(new Solution().alienOrder(new String[]{"wrt","wrf","er","ett","rftt"})); // Expected: "wertf"
}
}
// Main entry point delegates to Solution.main
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
["wrt","wrf","er","ett","rftt"] → "wertf"
Also asked: Amazon, Airbnb
D79. Task Scheduler
O(t)/O(1)Greedy formula / heapJava + JS lab
Min intervals to finish tasks with cooldown n between same letters.
Approach
(maxFreq-1)*(n+1) + count of tasks with maxFreq, then max with tasks.length.
Easy Algorithm
count freqs
maxf and cnt at maxf
max(len, (maxf-1)*(n+1)+cnt)
Code (Java)
import java.util.*;
// Main class to run the Task Scheduler solution
public class Main {
// Solution class implementing the Task Scheduler algorithm
// Uses greedy formula: max(tasks.length, (maxFreq-1)*(n+1) + countOfMaxFreq)
static class Solution {
public int leastInterval(char[] tasks,int n){int[] f=new int[26];for(char c:tasks)f[c-'A']++;
int maxf=0,cnt=0;for(int x:f){if(x>maxf){maxf=x;cnt=1;}else if(x==maxf)cnt++;}
return Math.max(tasks.length,(maxf-1)*(n+1)+cnt);}
public static void main(String[] args) {
System.out.println(new Solution().leastInterval(new char[]{'A','A','A','B','B','B'},2));
}
}
// Entry point: delegates to Solution.main for demonstration
public static void main(String[] args) {
Solution.main(args);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
import java.util.*;
public class Main {
// MedianFinder solution using two heaps: a max-heap (lo) for the lower half and a min-heap (hi) for the upper half
static class Solution {
// Inner class MF maintains two priority queues to find median in O(log n) per insertion
static class MF{PriorityQueue<Integer> lo=new PriorityQueue<>(Collections.reverseOrder()),hi=new PriorityQueue<>(); // lo = max-heap (lower half), hi = min-heap (upper half)
void addNum(int x){lo.add(x);hi.add(lo.poll());if(hi.size()>lo.size())lo.add(hi.poll());} // push to lo, move max to hi, rebalance if hi larger
double findMedian(){return lo.size()>hi.size()?lo.peek():(lo.peek()+hi.peek())/2.0;} // median is top of lo if odd, average of tops if even
}
// Executes a sequence of operations: ["MedianFinder"], ["addNum", val], ["findMedian"] and returns list of results (null for void ops)
public List<Object> runMedianFinder(List<List<Object>> ops){MF m=null;List<Object> out=new ArrayList<>();
for(List<Object> op:ops){String k=(String)op.get(0);if(k.equals("MedianFinder")){m=new MF();out.add(null);}
else if(k.equals("addNum")){m.addNum(((Number)op.get(1)).intValue());out.add(null);} else out.add(m.findMedian());}
return out;}
public static void main(String[] args) {
System.out.println(new Solution().runMedianFinder(List.of(/*ops*/)));
}
}
public static void main(String[] args) {
Solution.main(args); // the snippet's own demo: an empty list of operations gives []
// Stream 1, 2 then 3: the median is 1.5 after two numbers and 2.0 after three
List<List<Object>> ops = List.of(List.of("MedianFinder"), List.of("addNum", 1), List.of("addNum", 2),
List.of("findMedian"), List.of("addNum", 3), List.of("findMedian"));
System.out.println(new Solution().runMedianFinder(ops)); // [null, null, null, 1.5, null, 2.0]
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
add 1,2 → median 1.5; add 3 → 2
Also asked: Amazon, Google
D81. Maximum Product Subarray
O(n)/O(1)Track max/min endingJava + JS lab
Max product of a contiguous subarray.
Approach
Track max and min products ending here (sign flips).
Easy Algorithm
imax=imin=best=a[0]
for x: candidates x,imax*x,imin*x; update
Code (Java)
// Imports Java utility classes like List, Map, etc.
import java.util.*;
// Defines the outer Main class that holds the Solution inner class and a bootstrap main method.
public class Main {
// Declares a static inner class Solution containing the maxProduct algorithm and a demo main.
static class Solution {
// Defines maxProduct method that computes maximum product subarray using dynamic programming with imax/imin tracking.
public int maxProduct(int[] a){int imax=a[0],imin=a[0],best=a[0];
// Iterates through array updating imax (max product ending here), imin (min product ending here), and best overall max.
for(int i=1;i<a.length;i++){int x=a[i];int c1=imax*x,c2=imin*x;imax=Math.max(x,Math.max(c1,c2));imin=Math.min(x,Math.min(c1,c2));best=Math.max(best,imax);}return best;}
// Solution's main method serves as a demo entry point for testing maxProduct.
public static void main(String[] args) {
// Runs maxProduct on sample input [2,3,-2,4] and prints the result (expected 6).
System.out.println(new Solution().maxProduct(new int[]{2,3,-2,4}));
}
}
// Outer Main's main method delegates to Solution.main to execute the demo.
public static void main(String[] args){ Solution.main(args); }
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[2,3,-2,4]→6
Also asked: Amazon
D82. Candy
O(n)/O(n)Two-pass greedyJava + JS lab
Kids with ratings; give candies so higher rating than neighbor gets more; min total.
Approach
Left-to-right enforce left neighbor; right-to-left enforce right; take max.
Easy Algorithm
candies=1s
LTR then RTL peaks
sum
Code (Java)
// Import Java utility classes for array operations
import java.util.*;
// Define the outer Main class that holds the solution and entry point
public class Main {
/* Solution class implementing two-pass greedy candy distribution */
// Static nested class containing the candy distribution algorithm
static class Solution {
// candy method: initialize each child with 1 candy, then prepare for two passes
public int candy(int[] r){int n=r.length;int[] c=new int[n];Arrays.fill(c,1);
// Left-to-right pass: if current rating > left neighbor, give one more candy than left
for(int i=1;i<n;i++)if(r[i]>r[i-1])c[i]=c[i-1]+1;
// Right-to-left pass: if current rating > right neighbor, ensure at least right+1 candies
for(int i=n-2;i>=0;i--)if(r[i]>r[i+1])c[i]=Math.max(c[i],c[i+1]+1);
// Sum all candies and return the minimum total required
int s=0;for(int x:c)s+=x;return s;}
// Solution's main method for quick demonstration
public static void main(String[] args) {
// Test with ratings [1,0,2] expecting 5 candies (1+2+2)
System.out.println(new Solution().candy(new int[]{1,0,2}));
}
}
/* Entry point that delegates to Solution's main for demonstration */
// Main class entry point delegates to Solution.main for execution
public static void main(String[] args){ Solution.main(args); }
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[1,0,2]→5
Also asked: Amazon
D83. Gas Station
O(n)/O(1)Greedy circuitJava + JS lab
Unique start index to complete circuit, or -1.
Approach
If total gas<total cost impossible; else start after the worst deficit prefix.
Easy Algorithm
track tank and total
when tank<0 reset start=i+1 tank=0
Code (Java)
import java.util.*;
public class Main {
/** Main class to run the example. */
static class Solution {
/** Solution class containing the canCompleteCircuit method. */
/** Returns the starting gas station index if circuit possible, else -1. */
public int canCompleteCircuit(int[] gas,int[] cost){int tank=0,total=0,start=0;
for(int i=0;i<gas.length;i++){int d=gas[i]-cost[i];tank+=d;total+=d;if(tank<0){start=i+1;tank=0;}}return total<0?-1:start;}
/** Main method for testing the solution. */
public static void main(String[] args) {
System.out.println(new Solution().canCompleteCircuit(new int[]{1,2,3,4,5},new int[]{3,4,5,1,2}));
}
}
/** Entry point that invokes Solution's main. */
public static void main(String[] args){ Solution.main(args); }
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
gas=[1,2,3,4,5] cost=[3,4,5,1,2] → 3
Also asked: Amazon, Uber
D84. Jump Game
O(n)/O(1)Greedy farthestJava + JS lab
Can you reach last index given jump lengths?
Approach
Track farthest reachable; if i>farthest fail; update farthest.
Easy Algorithm
far=0
for i: if i>far false; far=max(far,i+a[i])
Code (Java)
import java.util.*;
public class Main {
// Solution class containing the Jump Game greedy algorithm
static class Solution {
// Greedy approach: track farthest reachable index
public boolean canJump(int[] a){int far=0;for(int i=0;i<a.length;i++){if(i>far)return false;far=Math.max(far,i+a[i]);}return true;}
public static void main(String[] args) {
System.out.println(new Solution().canJump(new int[]{2,3,1,1,4}));
}
}
// Entry point delegates to Solution.main
public static void main(String[] args){ Solution.main(args); }
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Can array be partitioned into two subsets with equal sum?
Approach
If total odd false; DP whether subset sum total/2 achievable.
Easy Algorithm
target=sum/2
boolean dp; update backward
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
Solution sol = new Solution();
// Test case from the snippet: [1,5,11,5] -> true (1+5+5 = 11)
System.out.println(sol.canPartition(new int[]{1,5,11,5})); // true
// Additional test: odd total sum -> false
System.out.println(sol.canPartition(new int[]{1,2,3,5})); // false
// Additional test: [3,3,3,4,5] sum=18 target=9 -> true (4+5=9)
System.out.println(sol.canPartition(new int[]{3,3,3,4,5})); // true
}
}
class Solution {
public boolean canPartition(int[] a){int s=0;for(int x:a)s+=x;if((s&1)==1)return false;int t=s/2;boolean[] dp=new boolean[t+1];dp[0]=true;
for(int x:a)for(int j=t;j>=x;j--)dp[j]|=dp[j-x];return dp[t];}
public static void main(String[] args) {
System.out.println(new Solution().canPartition(new int[]{1,5,11,5}));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[1,5,11,5] true
Also asked: Amazon
D86. Longest Common Subsequence
O(mn)/O(mn)2D DPJava + JS lab
Length of LCS of two strings.
Approach
dp[i][j]=diag+1 if equal else max(left,up).
Easy Algorithm
(m+1)x(n+1) table
fill; return dp[m][n]
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
// Create Solution instance and test with sample data
Solution solution = new Solution();
int result = solution.longestCommonSubsequence("abcde", "ace");
System.out.println("LCS length of 'abcde' and 'ace': " + result);
// Expected: 3 ("ace")
// Additional test cases
System.out.println("LCS length of 'abc' and 'abc': " + solution.longestCommonSubsequence("abc", "abc")); // 3
System.out.println("LCS length of 'abc' and 'def': " + solution.longestCommonSubsequence("abc", "def")); // 0
System.out.println("LCS length of 'bsbininm' and 'jmjkbkjkv': " + solution.longestCommonSubsequence("bsbininm", "jmjkbkjkv")); // 1
}
}
// Solution class kept exactly as provided in the snippet
class Solution {
public int longestCommonSubsequence(String a,String b){int m=a.length(),n=b.length();int[][] dp=new int[m+1][n+1];
for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)dp[i][j]=a.charAt(i-1)==b.charAt(j-1)?dp[i-1][j-1]+1:Math.max(dp[i-1][j],dp[i][j-1]);
return dp[m][n];}
public static void main(String[] args) {
System.out.println(new Solution().longestCommonSubsequence("abcde","ace"));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
abcde / ace → 3
Also asked: Amazon, Google
D87. Decode String
O(out)/O(nest)Stack of counts/stringsJava + JS lab
Decode k[encoded] nested pattern.
Approach
Stacks for counts and partial strings; on ']' expand.
Easy Algorithm
scan; digit→count; '[' push; ']' pop & repeat
Code (Java)
import java.util.*;
public class Main {
/**
* Main entry point to demonstrate the Solution.decodeString method.
*/
public static void main(String[] args) {
Solution solution = new Solution();
// Test case from the snippet
System.out.println("Test 1: " + solution.decodeString("3[a]2[bc]")); // expected "aaabcbc"
// Additional test cases
System.out.println("Test 2: " + solution.decodeString("3[a2[c]]")); // expected "accaccacc"
System.out.println("Test 3: " + solution.decodeString("2[abc]3[cd]ef")); // expected "abcabccdcdcdef"
}
}
/**
* Solution class implementing the decodeString algorithm using two stacks.
* This class is kept exactly as provided in the snippet.
*/
class Solution {
public String decodeString(String s){Deque<Integer> cnt=new ArrayDeque<>();Deque<StringBuilder> str=new ArrayDeque<>();
StringBuilder cur=new StringBuilder();int n=0;
for(char c:s.toCharArray()){if(Character.isDigit(c))n=n*10+c-'0';
else if(c=='['){cnt.push(n);str.push(cur);cur=new StringBuilder();n=0;}
else if(c==']'){StringBuilder prev=str.pop();int k=cnt.pop();for(int i=0;i<k;i++)prev.append(cur);cur=prev;}
else cur.append(c);}return cur.toString();}
public static void main(String[] args) {
System.out.println(new Solution().decodeString("3[a]2[bc]"));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
"3[a]2[bc]" → aaabcbc
Also asked: Amazon, Google
D88. Basic Calculator II
O(n)/O(n)Sign stack / runningJava + JS lab
Evaluate + - * / expression with nonneg ints (no parens).
Approach
Parse numbers; apply * / immediately to last term; + - push signed terms.
Easy Algorithm
last sign; on op flush number
sum stack at end
Code (Java)
import java.util.*; // Import Java utilities
// Main class to run sample calculations using the Solution class
public class Main {
public static void main(String[] args) {
Solution sol = new Solution(); // Create Solution instance
System.out.println("Test 1: 3+2*2 = " + sol.calculate("3+2*2")); // Expected 7
System.out.println("Test 2: 3/2 = " + sol.calculate("3/2")); // Expected 1
System.out.println("Test 3: 3+5/2 = " + sol.calculate("3+5/2")); // Expected 5
System.out.println("Test 4: 1+2*3-4/2 = " + sol.calculate("1+2*3-4/2")); // Expected 5
}
}
// Solution class implementing basic calculator II (handles + - * / without parentheses)
class Solution {
public int calculate(String s){Deque<Integer> st=new ArrayDeque<>();char sign='+';int num=0;s=s.replace(" ",""); // Initialize stack, sign, number, remove spaces
for(int i=0;i<s.length();i++){char c=s.charAt(i);if(Character.isDigit(c))num=num*10+c-'0'; // Loop through characters, build number
if(!Character.isDigit(c)||i==s.length()-1){if(sign=='+')st.push(num);else if(sign=='-')st.push(-num); // Process previous sign when operator or end
else if(sign=='*')st.push(st.pop()*num);else st.push(st.pop()/num);sign=c;num=0;}} // Handle * / immediately, update sign, reset number
int sum=0;for(int x:st)sum+=x;return sum;} // Sum stack for final result
public static void main(String[] args) { // Original test main
System.out.println(new Solution().calculate("3+2*2")); // Print result for 3+2*2
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
"3+2*2" → 7
Also asked: Amazon, Meta
D89. Simplify Path
O(n)/O(n)Stack of path partsJava + JS lab
Simplify Unix absolute path.
Approach
Split on /; push names; pop on '..'; ignore '.' and empties.
Easy Algorithm
stack parts
join with /; root if empty
Code (Java)
// Import Java utility classes for collections like Deque and ArrayDeque.
import java.util.*;
// Main class containing the demonstration of path simplification.
public class Main {
// Main method entry point for running demo test cases.
public static void main(String[] args) {
// Demo: simplify a Unix absolute path
// Create an instance of Solution to use its simplifyPath method.
Solution solution = new Solution();
// Define first test path with current directory, parent directory references.
String path1 = "/a/./b/../../c/";
// Print the input path for visibility.
System.out.println("Input: " + path1);
// Print the simplified path output from Solution.
System.out.println("Output: " + solution.simplifyPath(path1));
// Additional test cases
// Define a simple absolute path with trailing slash.
String path2 = "/home/";
// Print input path.
System.out.println("Input: " + path2);
// Print simplified output.
System.out.println("Output: " + solution.simplifyPath(path2));
// Define path that attempts to go above root directory.
String path3 = "/../";
// Print input path.
System.out.println("Input: " + path3);
// Print simplified output (should remain root).
System.out.println("Output: " + solution.simplifyPath(path3));
// Define path with multiple consecutive slashes.
String path4 = "/home//foo/";
// Print input path.
System.out.println("Input: " + path4);
// Print simplified output (extra slashes removed).
System.out.println("Output: " + solution.simplifyPath(path4));
}
}
// Original Solution class from the snippet (must appear exactly as written)
// Solution class providing Unix path simplification logic.
class Solution {
// Method simplifyPath takes a Unix absolute path and returns canonical path using a stack.
public String simplifyPath(String path){Deque<String> st=new ArrayDeque<>();
// Split path by '/', process each component: skip empty or '.', handle '..' by popping stack, else push directory.
for(String p:path.split("/")){if(p.equals("")||p.equals("."))continue;if(p.equals("..")){if(!st.isEmpty())st.pollLast();}else st.addLast(p);}
// Join stack with '/' and prepend root '/' to form canonical path.
return "/"+String.join("/",st);}
// Main method in Solution for quick standalone test (not used in demo).
public static void main(String[] args) {
// Run a sample simplification and print result.
System.out.println(new Solution().simplifyPath("/a/./b/../../c/"));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
import java.util.*;
class Solution {
static class Trie{static class N{N[] ch=new N[26];boolean end;} N root=new N();
void insert(String w){N n=root;for(char c:w.toCharArray()){int i=c-'a';if(n.ch[i]==null)n.ch[i]=new N();n=n.ch[i];}n.end=true;}
boolean search(String w){N n=find(w);return n!=null&&n.end;} boolean startsWith(String p){return find(p)!=null;}
N find(String w){N n=root;for(char c:w.toCharArray()){int i=c-'a';if(n.ch[i]==null)return null;n=n.ch[i];}return n;}}
public List<Object> runTrie(List<List<Object>> ops){Trie t=null;List<Object> out=new ArrayList<>();
for(List<Object> op:ops){String m=(String)op.get(0);if(m.equals("Trie")){t=new Trie();out.add(null);}
else if(m.equals("insert")){t.insert((String)op.get(1));out.add(null);}
else if(m.equals("search"))out.add(t.search((String)op.get(1))); else out.add(t.startsWith((String)op.get(1)));}
return out;}
public static void main(String[] args) {
System.out.println(new Solution().runTrie(/*ops*/));
}
}
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
dp[i]=true if prefix s[0..i) breakable; try dict words ending at i.
Easy Algorithm
dp[0]=true
for i: if some j with dp[j] and s[j..i) in dict
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.wordBreak("leetcode", List.of("leet", "code")));
System.out.println(sol.wordBreak("applepenapple", List.of("apple", "pen")));
System.out.println(sol.wordBreak("catsandog", List.of("cats", "dog", "sand", "and", "cat")));
}
}
class Solution {
public boolean wordBreak(String s,List<String> wordDict){Set<String> set=new HashSet<>(wordDict);boolean[] dp=new boolean[s.length()+1];dp[0]=true; // DP array, dp[0]=true means empty string is breakable
for(int i=1;i<=s.length();i++)for(int j=0;j<i;j++)if(dp[j]&&set.contains(s.substring(j,i))){dp[i]=true;break;}return dp[s.length()];} // Check all prefixes ending at i
public static void main(String[] args) { // Snippet's own main method
System.out.println(new Solution().wordBreak("leetcode", List.of("leet","code"))); // Prints true
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
leetcode / ["leet","code"] true
Also asked: Amazon, Google
D92. Surrounded Regions
O(mn)Border DFS then flipJava + JS lab
Capture surrounded 'O' regions by flipping to 'X' (border-safe O stay).
Approach
DFS from border O mark '#'; flip remaining O→X then '#'→O. Return board.
Easy Algorithm
mark border-connected O
flip
restore marks
Code (Java)
import java.util.*;
/**
* Main entry point demonstrating the Surrounded Regions solution.
* Creates a sample board, runs the capture algorithm, and prints the result.
*/
public class Main {
public static void main(String[] args) {
// Sample board: X = wall, O = region to potentially capture
char[][] board = {
{'X','X','X','X'},
{'X','O','O','X'},
{'X','X','O','X'},
{'X','O','X','X'}
};
System.out.println("Original board:");
printBoard(board);
Solution solver = new Solution();
char[][] result = solver.solve(board);
System.out.println("\nBoard after capturing surrounded regions:");
printBoard(result);
}
/** Helper to pretty-print a 2D char array. */
private static void printBoard(char[][] b) {
for (char[] row : b) {
System.out.println(Arrays.toString(row));
}
}
}
/**
* LeetCode 130 - Surrounded Regions.
* Captures all 'O' regions surrounded by 'X' by flipping them to 'X'.
* Regions connected to the border remain 'O'.
* Uses DFS from border 'O's to mark them safe ('#'), then flips the rest.
*/
class Solution {
public char[][] solve(char[][] b){int R=b.length,C=b[0].length; // R=rows, C=cols
for(int r=0;r<R;r++){dfs(b,r,0);dfs(b,r,C-1);}for(int c=0;c<C;c++){dfs(b,0,c);dfs(b,R-1,c);} // DFS from all four borders
for(int r=0;r<R;r++)for(int c=0;c<C;c++){if(b[r][c]=='O')b[r][c]='X';else if(b[r][c]=='#')b[r][c]='O';}return b;} // Flip unmarked O->X, restore #->O
void dfs(char[][] b,int r,int c){if(r<0||c<0||r>=b.length||c>=b[0].length||b[r][c]!='O')return;b[r][c]='#'; // Bounds check & skip non-O; mark safe
dfs(b,r+1,c);dfs(b,r-1,c);dfs(b,r,c+1);dfs(b,r,c-1);} // Recurse 4 directions
public static void main(String[] args) { // Original test harness
System.out.println(Arrays.deepToString(new Solution().solve(new char[][]{{'X','X','X','X'},{'X','O','O','X'},{'X','X','O','X'},{'X','O','X','X'}})));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
classic board capture
Also asked: Amazon, Google
D93. Accounts Merge
O(N α + E log E)Union emails + sortJava + JS lab
Merge accounts sharing an email; return name + sorted emails.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
classic LC merge
Also asked: Amazon, Meta
D94. Minimum Window Substring
O(|s|+|t|)Sliding window need/haveJava + JS lab
Smallest substring of s covering all chars in t (with multiplicity).
Approach
Expand right to satisfy need; shrink left; track best window.
Easy Algorithm
need map + missing count
move R; while valid move L; update best
Code (Java)
// Imports Java utility classes for collections and other utilities.
import java.util.*;
// Defines the Main class containing the program entry point.
public class Main {
// Main method where program execution begins.
public static void main(String[] args) {
// Creates an instance of Solution to call minWindow.
Solution sol = new Solution();
// Calls minWindow with sample input and stores the result.
String result = sol.minWindow("ADOBECODEBANC", "ABC");
// Prints the result of the first test case.
System.out.println("Result: " + result);
// Prints the result of a second test case with single character.
System.out.println("Test2: " + sol.minWindow("a", "a"));
}
}
// Defines the Solution class containing the minWindow algorithm.
class Solution {
// Declares minWindow method; initializes character needs array and missing count.
public String minWindow(String s,String t){int[] need=new int[128];int missing=t.length();
// Builds frequency map of characters needed from t; initializes best window indices and left pointer.
for(char c:t.toCharArray())need[c]++;int bestL=0,bestR=Integer.MAX_VALUE,L=0;
// Expands the window to the right, updating missing count and character needs.
for(int R=0;R<s.length();R++){char c=s.charAt(R);if(need[c]>0)missing--;need[c]--;
// Shrinks the window from the left while all characters are satisfied, updating best window.
while(missing==0){if(R-L<bestR-bestL){bestL=L;bestR=R;}need[s.charAt(L)]++;if(need[s.charAt(L)]>0)missing++;L++;}}
// Returns the smallest valid window substring or empty string if none found.
return bestR==Integer.MAX_VALUE?"":s.substring(bestL,bestR+1);}
// Additional main method in Solution for standalone testing.
public static void main(String[] args) {
// Runs a test case and prints the result directly.
System.out.println(new Solution().minWindow("ADOBECODEBANC","ABC"));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
s=ADOBECODEBANC t=ABC → BANC
Also asked: Amazon, Google
D95. Jump Game
O(n)/O(1)Greedy farthest reachJava + JS lab
Return true if you can reach last index; nums[i]=max jump from i.
Approach
Track farthest reachable index while scanning; fail if i exceeds farthest.
Easy Algorithm
farthest=0
for i: if i>farthest return false; farthest=max(farthest,i+nums[i])
return true
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
Solution sol = new Solution();
// Test case 1: can reach end
int[] nums1 = {2, 3, 1, 1, 4};
System.out.println("Test 1: " + sol.canJump(nums1)); // true
// Test case 2: cannot reach end
int[] nums2 = {3, 2, 1, 0, 4};
System.out.println("Test 2: " + sol.canJump(nums2)); // false
// Test case 3: single element
int[] nums3 = {0};
System.out.println("Test 3: " + sol.canJump(nums3)); // true
}
}
class Solution {
public boolean canJump(int[] nums){int far=0;for(int i=0;i<nums.length;i++){if(i>far)return false;far=Math.max(far,i+nums[i]);}return true;}
public static void main(String[] args){System.out.println(new Solution().canJump(new int[]{2,3,1,1,4}));}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D96. Gas Station
O(n)/O(1)Circuit tank greedyJava + JS lab
Circular route gas[i]/cost[i]; return start index or -1.
Approach
If total gas<cost impossible. Reset start when tank goes negative.
Easy Algorithm
if sum gas<cost return -1
tank=0 start=0
for i: tank+=gas-cost; if tank<0: start=i+1 tank=0
Code (Java)
import java.util.*;
public class Main {
/**
* Main class to demonstrate the gas station circuit problem.
*/
public static void main(String[] args) {
// Create an instance of Solution to use its method
Solution sol = new Solution();
// Sample gas and cost arrays
int[] gas = {1, 2, 3, 4, 5};
int[] cost = {3, 4, 5, 1, 2};
// Call the method and print the starting index
int result = sol.canCompleteCircuit(gas, cost);
System.out.println("Starting index: " + result);
}
}
class Solution { // Solution class for gas station problem
public int canCompleteCircuit(int[] gas,int[] cost){int total=0,tank=0,start=0;for(int i=0;i<gas.length;i++){total+=gas[i]-cost[i];tank+=gas[i]-cost[i];if(tank<0){start=i+1;tank=0;}}return total<0?-1:start;} // greedy algorithm to find starting index
public static void main(String[] args){System.out.println(new Solution().canCompleteCircuit(new int[]{1,2,3,4,5},new int[]{3,4,5,1,2}));} // demo main method
} // end of Solution class
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D97. Longest Consecutive Sequence
O(n)/O(n)Hash set streaksJava + JS lab
Longest consecutive sequence length in O(n).
Approach
Put in set; only start streak when num-1 absent; count forward.
Easy Algorithm
set=all
start only if x-1 missing
extend while x+len present; track max
Code (Java)
import java.util.*;
// Main class to demonstrate the longestConsecutive algorithm
public class Main {
// Entry point: runs demo test cases
public static void main(String[] args) {
Solution sol = new Solution(); // Create Solution instance
int[] test1 = {100, 4, 200, 1, 3, 2}; // Example with streak 1-4
System.out.println("Test 1: " + sol.longestConsecutive(test1)); // Expected: 4
int[] test2 = {0, 3, 7, 2, 5, 8, 4, 6, 0, 1}; // Example with streak 0-8
System.out.println("Test 2: " + sol.longestConsecutive(test2)); // Expected: 9
int[] test3 = {}; // Empty array edge case
System.out.println("Test 3: " + sol.longestConsecutive(test3)); // Expected: 0
}
}
// Solution class containing the O(n) longest consecutive sequence algorithm
class Solution {
public int longestConsecutive(int[] nums){Set<Integer> s=new HashSet<>();for(int n:nums)s.add(n);int best=0;for(int n:s){if(!s.contains(n-1)){int len=1;while(s.contains(n+len))len++;best=Math.max(best,len);}}return best;} // Core algorithm: hash set streaks
public static void main(String[] args){System.out.println(new Solution().longestConsecutive(new int[]{100,4,200,1,3,2}));} // Snippet's own demo main
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D98. Decode String
O(n)/O(n)Stack count+chunkJava + JS lab
Decode nested k[encoded] patterns.
Approach
Stack (prev,k); on ] expand chunk.repeat(k) onto prev.
Easy Algorithm
parse digits to k
[: push buf+k reset
]: pop expand
else append
Code (Java)
import java.util.*;
public class Main {
/**
* Main class to demonstrate the decodeString functionality.
*/
public static void main(String[] args) {
Solution solver = new Solution(); // create instance of Solution
// Test case 1: nested encoding
String input1 = "3[a2[c]]";
String output1 = solver.decodeString(input1);
System.out.println("Input: " + input1 + " -> Output: " + output1); // expected: accaccacc
// Test case 2: simple encoding
String input2 = "2[abc]3[cd]ef";
String output2 = solver.decodeString(input2);
System.out.println("Input: " + input2 + " -> Output: " + output2); // expected: abcabccdcdcdef
// Test case 3: no encoding
String input3 = "abc";
String output3 = solver.decodeString(input3);
System.out.println("Input: " + input3 + " -> Output: " + output3); // expected: abc
}
}
/**
* Solution class containing the decodeString method to decode nested k[encoded] patterns.
*/
class Solution {
public String decodeString(String s){Deque<Integer> ks=new ArrayDeque<>();Deque<StringBuilder> strs=new ArrayDeque<>();StringBuilder cur=new StringBuilder();int k=0; // decode method
for(char c:s.toCharArray()){if(Character.isDigit(c))k=k*10+c-'0';else if(c=='['){ks.push(k);strs.push(cur);cur=new StringBuilder();k=0;}else if(c==']'){StringBuilder prev=strs.pop();int rep=ks.pop();for(int i=0;i<rep;i++)prev.append(cur);cur=prev;}else cur.append(c);}return cur.toString();} // decode logic
public static void main(String[] args){System.out.println(new Solution().decodeString("3[a2[c]]"));} // snippet's own main
} // end of Solution class
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D99. Asteroid Collision
O(n)/O(n)Stack survivorsJava + JS lab
+/- asteroids collide; larger wins; equal both destroy.
Approach
Push; on left-mover collide while top is right-moving.
Easy Algorithm
for each asteroid
while opposite collision: compare abs
push survivor
Code (Java)
import java.util.*;
/**
* Main class demonstrating the asteroid collision solution.
*/
public class Main {
/**
* Entry point: runs several test cases for asteroidCollision.
*/
public static void main(String[] args) { // Demo asteroidCollision with sample data
Solution sol = new Solution(); // Create solution instance
int[] result1 = sol.asteroidCollision(new int[]{5, 10, -5}); // Test case 1
System.out.println("Test 1: " + Arrays.toString(result1)); // Print result
int[] result2 = sol.asteroidCollision(new int[]{8, -8}); // Test case 2
System.out.println("Test 2: " + Arrays.toString(result2)); // Print result
int[] result3 = sol.asteroidCollision(new int[]{10, 2, -5}); // Test case 3
System.out.println("Test 3: " + Arrays.toString(result3)); // Print result
}
}
class Solution { // Snippet class for asteroid collision logic
public int[] asteroidCollision(int[] asteroids){Deque<Integer> st=new ArrayDeque<>();for(int a:asteroids){boolean push=true;while(!st.isEmpty()&&st.peek()>0&&a<0){int top=st.peek();if(top<-a){st.pop();continue;}if(top==-a)st.pop();push=false;break;}if(push)st.push(a);}int[] res=new int[st.size()];for(int i=res.length-1;i>=0;i--)res[i]=st.pop();return res;} // Core algorithm using stack
public static void main(String[] args){System.out.println(Arrays.toString(new Solution().asteroidCollision(new int[]{5,10,-5})));} // Snippet's own main method
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D100. Task Scheduler
O(n)/O(1)Idle slots by max freqJava + JS lab
Tasks with cooldown n; min intervals to finish.
Approach
Frame from max freq; fill idle with other tasks; ans=len+remaining idle.
Easy Algorithm
f=max freq
idle=(f-1)*n
subtract other fills
return n+max(0,idle)
Code (Java)
import java.util.*;
// Main class to demonstrate the task scheduler solution
public class Main {
public static void main(String[] args) {
Solution solver = new Solution(); // Create instance of Solution
// Test case 1: tasks = ["A","A","A","B","B","B"], n = 2
char[] tasks1 = {'A','A','A','B','B','B'};
int n1 = 2;
int result1 = solver.leastInterval(tasks1, n1);
System.out.println("Test 1 (AAA BBB, n=2): " + result1); // Expected 8
// Test case 2: tasks = ["A","A","A","B","B","B"], n = 0
char[] tasks2 = {'A','A','A','B','B','B'};
int n2 = 0;
int result2 = solver.leastInterval(tasks2, n2);
System.out.println("Test 2 (AAA BBB, n=0): " + result2); // Expected 6
// Test case 3: tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2
char[] tasks3 = {'A','A','A','A','A','A','B','C','D','E','F','G'};
int n3 = 2;
int result3 = solver.leastInterval(tasks3, n3);
System.out.println("Test 3 (6A + BCDEFG, n=2): " + result3); // Expected 16
}
}
// Solution class implementing the leastInterval method for task scheduling with cooldown
class Solution {
public int leastInterval(char[] tasks,int n){int[] cnt=new int[26];for(char c:tasks)cnt[c-'A']++;Arrays.sort(cnt);int f=cnt[25];int idle=(f-1)*n;for(int i=24;i>=0&&cnt[i]>0;i--)idle-=Math.min(cnt[i],f-1);return tasks.length+Math.max(0,idle);} // Count frequencies, sort, calculate idle slots
public static void main(String[] args){System.out.println(new Solution().leastInterval(new char[]{'A','A','A','B','B','B'},2));} // Original snippet main method
} // End of Solution class
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D101. Partition Equal Subset Sum
O(n*sum)/O(sum)0/1 knapsack bitsetJava + JS lab
Can array split into two equal-sum subsets?
Approach
Target=sum/2; DP reachable sums with boolean array (or bitset).
Easy Algorithm
if sum odd false
dp[0]=true
for num: update dp backward
return dp[target]
Code (Java)
import java.util.*;
/**
* Main class to demonstrate the canPartition method.
*/
public class Main {
/**
* Entry point: runs several test cases for the partition equal subset sum problem.
*/
public static void main(String[] args) {
Solution sol = new Solution(); // create instance of Solution
// Test case 1: can partition into {1,5,5} and {11}
int[] nums1 = {1, 5, 11, 5};
System.out.println("Test 1 (expect true): " + sol.canPartition(nums1));
// Test case 2: cannot partition
int[] nums2 = {1, 2, 3, 5};
System.out.println("Test 2 (expect false): " + sol.canPartition(nums2));
// Test case 3: single element
int[] nums3 = {2};
System.out.println("Test 3 (expect false): " + sol.canPartition(nums3));
// Test case 4: empty array (sum=0, trivially partitionable)
int[] nums4 = {};
System.out.println("Test 4 (expect true): " + sol.canPartition(nums4));
}
}
/**
* Solution class containing the 0/1 knapsack DP for partition equal subset sum.
*/
class Solution {
public boolean canPartition(int[] nums){int sum=0;for(int n:nums)sum+=n;if((sum&1)==1)return false;int t=sum/2;boolean[] dp=new boolean[t+1];dp[0]=true;for(int n:nums)for(int s=t;s>=n;s--)dp[s]|=dp[s-n];return dp[t];} // DP solution
public static void main(String[] args){System.out.println(new Solution().canPartition(new int[]{1,5,11,5}));} // original demo
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Outer coins / inner amounts — order ensures combinations not permutations.
Easy Algorithm
dp[0]=1
for coin: for a=coin..amount: dp[a]+=dp[a-coin]
return dp[amount]
Code (Java)
import java.util.*;
// Main class to demonstrate the Coin Change II solution
public class Main {
public static void main(String[] args) {
// Create an instance of Solution
Solution sol = new Solution();
// Sample amount and coins
int amount = 5;
int[] coins = {1, 2, 5};
// Compute the number of ways to make change
int result = sol.change(amount, coins);
// Print the result
System.out.println("Number of ways to make change for " + amount + " with coins " + Arrays.toString(coins) + ": " + result);
}
}
// Solution class implementing the coin change algorithm (unbounded knapsack DP)
class Solution {
public int change(int amount,int[] coins){int[] dp=new int[amount+1];dp[0]=1;for(int c:coins)for(int a=c;a<=amount;a++)dp[a]+=dp[a-c];return dp[amount];} // DP solution for coin change II
public static void main(String[] args){System.out.println(new Solution().change(5,new int[]{1,2,5}));} // Example usage from the snippet
} // End of Solution class
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D103. Unique Paths II
O(mn)/O(mn)Grid DP with obstaclesJava + JS lab
Robot paths top-left to bottom-right with obstacles.
Approach
dp[0][0]=1 if free; first row/col blocked propagate; dp[i][j]=sum from up/left if free.
Easy Algorithm
if start blocked 0
init borders until obstacle
dp cells = up+left if empty
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
Solution sol = new Solution(); // Create Solution instance
int[][] grid1 = {{0,0,0},{0,1,0},{0,0,0}}; // 3x3 grid with obstacle at center
System.out.println("Grid 3x3 with center obstacle: " + sol.uniquePathsWithObstacles(grid1));
int[][] grid2 = {{0}}; // 1x1 grid no obstacle
System.out.println("1x1 no obstacle: " + sol.uniquePathsWithObstacles(grid2));
int[][] grid3 = {{1}}; // 1x1 grid with obstacle
System.out.println("1x1 with obstacle: " + sol.uniquePathsWithObstacles(grid3));
int[][] grid4 = {{0,0},{0,0}}; // 2x2 grid no obstacles
System.out.println("2x2 no obstacles: " + sol.uniquePathsWithObstacles(grid4));
}
}
class Solution {
public int uniquePathsWithObstacles(int[][] g){int m=g.length,n=g[0].length;if(g[0][0]==1)return 0;int[][] dp=new int[m][n];dp[0][0]=1; // Initialize DP, return 0 if start blocked
for(int i=0;i<m;i++)for(int j=0;j<n;j++){if(g[i][j]==1){dp[i][j]=0;continue;}if(i==0&&j==0)continue;int up=i>0?dp[i-1][j]:0,left=j>0?dp[i][j-1]:0;dp[i][j]=up+left;}return dp[m-1][n-1];} // DP fill: skip obstacles, sum from up and left
public static void main(String[] args){System.out.println(new Solution().uniquePathsWithObstacles(new int[][]{{0,0,0},{0,1,0},{0,0,0}}));} // Original snippet demo
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D104. Minimum Path Sum
O(mn)/O(mn)Grid DP min costJava + JS lab
Min path sum top-left to bottom-right moving right/down.
Approach
dp[i][j]=grid+min(up,left) with border prefix fills.
Easy Algorithm
dp[0][0]=grid
fill first row/col cumulatively
interior = cell+min(up,left)
Code (Java)
import java.util.*;
// Main class to demonstrate the minimum path sum algorithm
public class Main {
public static void main(String[] args) {
Solution sol = new Solution();
int[][] grid = {{1,3,1},{1,5,1},{4,2,1}};
int result = sol.minPathSum(grid);
System.out.println("Min path sum: " + result);
}
}
class Solution {
public int minPathSum(int[][] g){int m=g.length,n=g[0].length;int[][] dp=new int[m][n];dp[0][0]=g[0][0]; // initialize dp grid and set starting cell
for(int i=1;i<m;i++)dp[i][0]=dp[i-1][0]+g[i][0];for(int j=1;j<n;j++)dp[0][j]=dp[0][j-1]+g[0][j]; // fill first column and first row
for(int i=1;i<m;i++)for(int j=1;j<n;j++)dp[i][j]=g[i][j]+Math.min(dp[i-1][j],dp[i][j-1]);return dp[m-1][n-1];} // fill rest of dp grid and return bottom-right value
public static void main(String[] args){System.out.println(new Solution().minPathSum(new int[][]{{1,3,1},{1,5,1},{4,2,1}}));} // demo main in Solution class
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D105. Sort Colors
O(n)/O(1)Dutch national flagJava + JS lab
Sort array of 0/1/2 in-place one pass.
Approach
Three pointers lo/mid/hi; swap 0s left, 2s right, advance on 1.
Easy Algorithm
lo=0 mid=0 hi=n-1
while mid<=hi: 0→swap lo++; 2→swap hi--; 1→mid++
Code (Java)
// Import Java utility classes including Arrays for printing arrays.
import java.util.*;
// Define the Main class containing the program entry point.
public class Main {
// Main method that runs the test cases.
public static void main(String[] args) {
// Create an instance of the Solution class to call sortColors.
Solution sol = new Solution();
// First test input array containing 0s, 1s, and 2s in random order.
int[] test1 = {2,0,2,1,1,0};
// Call sortColors on test1 and store the sorted result.
int[] result1 = sol.sortColors(test1);
// Print the sorted array for test 1.
System.out.println("Test 1: " + Arrays.toString(result1));
// Second test input array with a different arrangement.
int[] test2 = {2,2,1,0,1,0};
// Call sortColors on test2 and store the sorted result.
int[] result2 = sol.sortColors(test2);
// Print the sorted array for test 2.
System.out.println("Test 2: " + Arrays.toString(result2));
}
}
// Solution class containing the sorting algorithm implementation.
class Solution {
// sortColors method using Dutch National Flag algorithm with three pointers (lo, mid, hi) to sort 0s, 1s, 2s in-place in a single pass.
public int[] sortColors(int[] nums){int lo=0,mid=0,hi=nums.length-1;while(mid<=hi){if(nums[mid]==0){int t=nums[lo];nums[lo]=nums[mid];nums[mid]=t;lo++;mid++;}else if(nums[mid]==2){int t=nums[hi];nums[hi]=nums[mid];nums[mid]=t;hi--;}else mid++;}return nums;}
// Additional main method inside Solution for standalone demonstration.
public static void main(String[] args){System.out.println(Arrays.toString(new Solution().sortColors(new int[]{2,0,2,1,1,0})));}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D106. K Closest Points to Origin
O(n log k)/O(k)Max-heap of size KJava + JS lab
K points closest to (0,0).
Approach
Max-heap by distance keep size k; or quickselect.
Easy Algorithm
push points to max-heap by dist
pop when size>k
return heap contents
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) { // demo kClosest with sample data
Solution sol = new Solution();
int[][] points = {{3,3}, {5,-1}, {-2,4}, {0,1}, {-1,0}};
int k = 3;
int[][] result = sol.kClosest(points, k);
System.out.println("K closest points: " + Arrays.deepToString(result));
}
}
class Solution { // solution class for k closest points
public int[][] kClosest(int[][] points,int k){PriorityQueue<int[]> pq=new PriorityQueue<>((a,b)->Long.compare((long)b[0]*b[0]+(long)b[1]*b[1],(long)a[0]*a[0]+(long)a[1]*a[1])); // max-heap by distance squared
for(int[] p:points){pq.offer(p);if(pq.size()>k)pq.poll();}return pq.toArray(new int[0][]);} // keep only k closest
public static void main(String[] args){System.out.println(Arrays.deepToString(new Solution().kClosest(new int[][]{{1,3},{-2,2}},1)));} // original demo
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Uber
D107. Flood Fill
O(mn)DFS/BFS recolorJava + JS lab
Recolor connected component from (sr,sc) to newColor.
Approach
DFS/BFS 4-dir from start; only recolor original color cells.
Easy Algorithm
save old color; if same as new return
DFS 4-dir when in-bound and ==old
set newColor
Code (Java)
import java.util.*;
/*
* Main entry point: demonstrates the floodFill algorithm on a sample image grid.
*/
public class Main {
public static void main(String[] args) { // program starts here
Solution sol = new Solution(); // create solution instance
int[][] image = {{1,1,1},{1,1,0},{1,0,1}}; // 3x3 input grid
int[][] result = sol.floodFill(image, 1, 1, 2); // fill from center (1,1) with color 2
System.out.println(Arrays.deepToString(result)); // print resulting grid
}
}
/*
* Solution class containing the floodFill implementation using depth-first search.
*/
class Solution {
public int[][] floodFill(int[][] image,int sr,int sc,int color){int old=image[sr][sc];if(old==color)return image;dfs(image,sr,sc,old,color);return image;} // entry: capture original color, early exit if same, start DFS
void dfs(int[][] g,int r,int c,int old,int col){if(r<0||c<0||r>=g.length||c>=g[0].length||g[r][c]!=old)return;g[r][c]=col;dfs(g,r+1,c,old,col);dfs(g,r-1,c,old,col);dfs(g,r,c+1,old,col);dfs(g,r,c-1,old,col);} // DFS: bounds & color check, paint, recurse 4 directions
public static void main(String[] args){System.out.println(Arrays.deepToString(new Solution().floodFill(new int[][]{{1,1,1},{1,1,0},{1,0,1}},1,1,2)));} // self-test main (not used when run via Main)
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D108. Max Area of Island
O(mn)DFS area accumulateJava + JS lab
Max 4-connected 1s island area in grid.
Approach
DFS each unvisited 1; sum area; track global max.
Easy Algorithm
scan cells
on land: dfs mark 0 and return 1+neighbors
track max area
Code (Java)
// Imports Java utility classes for collections and other utilities.
import java.util.*;
// Defines the Main class containing the program entry point.
public class Main {
// Main method where program execution begins.
public static void main(String[] args) {
// Demo maxAreaOfIsland with sample grid
// Creates an instance of the Solution class to access its methods.
Solution sol = new Solution();
// Initializes a 2D grid representing water (0) and land (1) for testing.
int[][] grid = {{0,0,1,0,0},{0,0,0,0,0},{0,0,1,1,1}};
// Calls maxAreaOfIsland to compute the largest island area in the grid.
int result = sol.maxAreaOfIsland(grid);
// Outputs the computed maximum island area to the console.
System.out.println("Max area of island: " + result);
}
}
// Defines the Solution class containing the island area calculation logic.
class Solution {
// Public method that scans the grid and uses DFS to find the maximum island area.
public int maxAreaOfIsland(int[][] g){int best=0;for(int r=0;r<g.length;r++)for(int c=0;c<g[0].length;c++)if(g[r][c]==1)best=Math.max(best,dfs(g,r,c));return best;}
// Private DFS helper that recursively explores connected land cells, marking them visited by setting to 0, and returns the area of the current island.
int dfs(int[][] g,int r,int c){if(r<0||c<0||r>=g.length||c>=g[0].length||g[r][c]==0)return 0;g[r][c]=0;return 1+dfs(g,r+1,c)+dfs(g,r-1,c)+dfs(g,r,c+1)+dfs(g,r,c-1);}
// Additional main method inside Solution for standalone testing of the algorithm.
public static void main(String[] args){System.out.println(new Solution().maxAreaOfIsland(new int[][]{{0,0,1,0,0},{0,0,0,0,0},{0,0,1,1,1}}));}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D109. Keys and Rooms
O(N+E)BFS/DFS reachabilityJava + JS lab
Rooms[i]=keys in room i; start room 0; can visit all?
Approach
BFS/DFS from 0 collecting keys; visited size==n.
Easy Algorithm
queue/stack from 0
visit + enqueue keys
return visited==n
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
Solution sol = new Solution();
// Test case 1: can visit all rooms
List<List<Integer>> rooms1 = List.of(List.of(1), List.of(2), List.of(3), List.of());
System.out.println("Test 1 (expect true): " + sol.canVisitAllRooms(rooms1));
// Test case 2: cannot visit all rooms (room 2 unreachable)
List<List<Integer>> rooms2 = List.of(List.of(1), List.of(), List.of(3), List.of());
System.out.println("Test 2 (expect false): " + sol.canVisitAllRooms(rooms2));
// Test case 3: single room
List<List<Integer>> rooms3 = List.of(List.of());
System.out.println("Test 3 (expect true): " + sol.canVisitAllRooms(rooms3));
}
}
class Solution {
public boolean canVisitAllRooms(List<List<Integer>> rooms){boolean[] seen=new boolean[rooms.size()];Deque<Integer> q=new ArrayDeque<>();q.add(0);seen[0]=true;int vis=1; // BFS initialization
while(!q.isEmpty()){int u=q.poll();for(int k:rooms.get(u))if(!seen[k]){seen[k]=true;vis++;q.add(k);}}return vis==rooms.size();} // BFS loop and result
public static void main(String[] args){System.out.println(new Solution().canVisitAllRooms(List.of(List.of(1),List.of(2),List.of(3),List.of())));} // Original demo main
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D110. Is Graph Bipartite
O(V+E)2-color BFSJava + JS lab
Return true if undirected graph is bipartite.
Approach
BFS/DFS 2-color each component; conflict ⇒ false.
Easy Algorithm
color array -1
BFS assign opposite color to neighbors
conflict if same
Code (Java)
import java.util.*;
/** Main class to demonstrate the Solution.isBipartite method. */
public class Main {
/** Runs demonstration of bipartite graph checking. */
public static void main(String[] args) {
// Demo 1: bipartite graph (even cycle)
int[][] graph1 = {{1,3}, {0,2}, {1,3}, {0,2}};
Solution sol = new Solution();
System.out.println("Graph1 (even cycle) is bipartite: " + sol.isBipartite(graph1));
// Demo 2: non-bipartite graph (odd cycle)
int[][] graph2 = {{1,2,3}, {0,2}, {0,1,3}, {0,2}};
System.out.println("Graph2 (odd cycle) is bipartite: " + sol.isBipartite(graph2));
}
}
class Solution {
public boolean isBipartite(int[][] graph){int n=graph.length;int[] col=new int[n];Arrays.fill(col,-1);
for(int i=0;i<n;i++){if(col[i]!=-1)continue;Deque<Integer> q=new ArrayDeque<>();q.add(i);col[i]=0;
while(!q.isEmpty()){int u=q.poll();for(int v:graph[u]){if(col[v]==-1){col[v]=col[u]^1;q.add(v);}else if(col[v]==col[u])return false;}}}return true;}
public static void main(String[] args){System.out.println(new Solution().isBipartite(new int[][]{{1,2,3},{0,2},{0,1,3},{0,2}}));}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D111. Course Schedule II
O(V+E)Kahn topo orderJava + JS lab
Return a valid course order or empty if cycle.
Approach
Kahn BFS: indegrees, queue zeros, append order; if count<n cycle.
Easy Algorithm
build graph+indegree
queue indegree0
pop append; reduce neighbors
Code (Java)
// Imports Java utility classes like ArrayList, Deque, and Arrays for data structures and printing.
import java.util.*;
// Defines the Main class containing the program entry point.
public class Main {
// Main method that runs the demo for the course schedule problem.
public static void main(String[] args) {
// Creates an instance of Solution to call the topological sort method.
Solution sol = new Solution();
// Calls findOrder with 2 courses and one prerequisite: course 1 requires course 0.
int[] result = sol.findOrder(2, new int[][]{{1,0}});
// Prints the resulting course order array to the console.
System.out.println(Arrays.toString(result));
}
}
// Solution class that implements topological sorting using Kahn's BFS algorithm.
class Solution {
// findOrder method: builds adjacency list g and indegree array ind; initializes empty lists for each node.
public int[] findOrder(int n,int[][] edges){List<List<Integer>> g=new ArrayList<>();int[] ind=new int[n];for(int i=0;i<n;i++)g.add(new ArrayList<>());
// Populates graph edges and indegrees from prerequisites; initializes queue with all zero-indegree nodes.
for(int[] e:edges){g.get(e[1]).add(e[0]);ind[e[0]]++;}Deque<Integer> q=new ArrayDeque<>();for(int i=0;i<n;i++)if(ind[i]==0)q.add(i);
// Processes queue in BFS order, building topological order ord; returns order if all nodes processed, else empty array.
int[] ord=new int[n];int k=0;while(!q.isEmpty()){int u=q.poll();ord[k++]=u;for(int v:g.get(u))if(--ind[v]==0)q.add(v);}return k==n?ord:new int[]{};}
// Alternative main method inside Solution class for direct testing; prints result for same 2-course example.
public static void main(String[] args){System.out.println(Arrays.toString(new Solution().findOrder(2,new int[][]{{1,0}})));}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D112. Connected Components
O(n+α)Union-Find on edgesJava + JS lab
n nodes and undirected edges; return component count.
Approach
UF with path compression; count roots after unions.
Easy Algorithm
parent[i]=i
union edges
count unique finds
Code (Java)
import java.util.*;
import java.util.function.*;
public class Main {
// Solution class implements Union-Find (Disjoint Set Union) to count connected components in an undirected graph
static class Solution {
// Original countComponents method: initializes parent array, defines find with path compression, unions edges, returns component count
public int countComponents(int n,int[][] edges){int[] p=new int[n];for(int i=0;i<n;i++)p[i]=i; // Initialize each node as its own parent
java.util.function.IntUnaryOperator find=new java.util.function.IntUnaryOperator(){public int applyAsInt(int x){return p[x]==x?x:(p[x]=applyAsInt(p[x]));}}; // Find with path compression
int c=n;for(int[] e:edges){int a=find.applyAsInt(e[0]),b=find.applyAsInt(e[1]);if(a!=b){p[a]=b;c--;}}return c;} // Union each edge, decrement count when merging
public static void main(String[] args){System.out.println(new Solution().countComponents(5,new int[][]{{0,1},{1,2},{3,4}}));} // Original test case
}
// Main entry point: exercises Solution with the same sample graph and prints result
public static void main(String[] args) {
Solution solution = new Solution();
int components = solution.countComponents(5, new int[][]{{0,1},{1,2},{3,4}});
System.out.println("Number of connected components: " + components); // Expected output: 2
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D113. Redundant Connection
O(n α)UF first cycle edgeJava + JS lab
Return edge that creates a cycle (tree + 1 edge).
Approach
Union edges; first already-connected edge is answer.
Easy Algorithm
UF
if same root return edge
else union
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
Solution sol = new Solution();
int[][] edges1 = {{1,2},{1,3},{2,3}};
System.out.println(Arrays.toString(sol.findRedundantConnection(edges1))); // [2, 3]
int[][] edges2 = {{1,2},{2,3},{3,4},{1,4},{1,5}};
System.out.println(Arrays.toString(sol.findRedundantConnection(edges2))); // [1, 4]
}
}
class Solution {
public int[] findRedundantConnection(int[][] edges){int n=edges.length;int[] p=new int[n+1];for(int i=0;i<=n;i++)p[i]=i; // initialize parent array
java.util.function.IntUnaryOperator find=new java.util.function.IntUnaryOperator(){public int applyAsInt(int x){return p[x]==x?x:(p[x]=applyAsInt(p[x]));}}; // find with path compression
for(int[] e:edges){int a=find.applyAsInt(e[0]),b=find.applyAsInt(e[1]);if(a==b)return e;p[a]=b;}return new int[]{};} // union-find cycle detection
public static void main(String[] args){System.out.println(Arrays.toString(new Solution().findRedundantConnection(new int[][]{{1,2},{1,3},{2,3}})));} // original demo
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D114. Network Delay Time
O(E log V)DijkstraJava + JS lab
n nodes, times edges; delay from k until all receive or -1.
Approach
Dijkstra from k; answer max dist among reachable.
Easy Algorithm
adj + minheap
relax
max dist or -1
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
Solution sol = new Solution();
int[][] times = {{2,1,1},{2,3,1},{3,4,1}};
int n = 4;
int k = 2;
int result = sol.networkDelayTime(times, n, k);
System.out.println("Network delay time: " + result);
}
}
class Solution {
public int networkDelayTime(int[][] times,int n,int k){List<int[]>[] g=new ArrayList[n+1];for(int i=1;i<=n;i++)g[i]=new ArrayList<>(); // Build graph adjacency list
for(int[] t:times)g[t[0]].add(new int[]{t[1],t[2]});int[] dist=new int[n+1];Arrays.fill(dist,Integer.MAX_VALUE);dist[k]=0; // Add edges, init distances, source distance = 0
PriorityQueue<int[]> pq=new PriorityQueue<>(Comparator.comparingInt(a->a[0]));pq.offer(new int[]{0,k}); // Min-heap by distance, start from source
while(!pq.isEmpty()){int[] cur=pq.poll();int d=cur[0],u=cur[1];if(d!=dist[u])continue;for(int[] e:g[u]){int v=e[0],w=e[1];if(dist[v]>d+w){dist[v]=d+w;pq.offer(new int[]{dist[v],v});}}} // Dijkstra main loop
int ans=0;for(int i=1;i<=n;i++){if(dist[i]==Integer.MAX_VALUE)return -1;ans=Math.max(ans,dist[i]);}return ans;} // Find max delay or -1 if any node unreachable
public static void main(String[] args){System.out.println(new Solution().networkDelayTime(new int[][]{{2,1,1},{2,3,1},{3,4,1}},4,2));} // Original demo
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D115. Cheapest Flights Within K Stops
O(kE)Bellman k+1Java + JS lab
Cheapest src→dst with ≤k stops.
Approach
Bellman-Ford k+1 rounds with snapshot array.
Easy Algorithm
dist[src]=0
relax k+1 times from snapshot
return dist[dst]
Code (Java)
import java.util.*;
/* Main class to demonstrate the cheapest flights solution */
public class Main {
/* Demonstrates finding cheapest price with up to k stops */
public static void main(String[] args) {
Solution sol = new Solution();
int n = 4;
int[][] flights = {{0,1,100},{1,2,100},{2,0,100},{1,3,600},{2,3,200}};
int src = 0, dst = 3, k = 1;
int result = sol.findCheapestPrice(n, flights, src, dst, k);
System.out.println("Cheapest price: " + result);
}
}
/* Solution class implementing Bellman-Ford for cheapest flights within k stops */
class Solution { // Solution class for cheapest flights
public int findCheapestPrice(int n,int[][] flights,int src,int dst,int k){int[] dist=new int[n];Arrays.fill(dist,Integer.MAX_VALUE/4);dist[src]=0; // Initialize distances
for(int i=0;i<=k;i++){int[] next=dist.clone();for(int[] f:flights){int u=f[0],v=f[1],w=f[2];if(dist[u]+w<next[v])next[v]=dist[u]+w;}dist=next;} // Relax edges up to k+1 times
return dist[dst]>=Integer.MAX_VALUE/4?-1:dist[dst];} // Return -1 if unreachable
public static void main(String[] args){System.out.println(new Solution().findCheapestPrice(4,new int[][]{{0,1,100},{1,2,100},{2,0,100},{1,3,600},{2,3,200}},0,3,1));} // Demo main
} // End of Solution class
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D116. Shortest Path Binary Matrix
O(n²)BFS 8-dirJava + JS lab
Shortest clear path in binary grid; -1 if impossible.
Approach
Equal-weight grid path ⇒ BFS (8-connected). Mark visited when enqueued to avoid duplicates; first time you reach the end is the shortest length (Job Prep matrix/BFS pattern family with Islands/Oranges).
Easy Algorithm
If start or end blocked ⇒ -1; n=1 ⇒ 1.
BFS from (0,0) with dist=1; explore 8 neighbors that are 0.
Mark cell visited on enqueue; return dist when (n-1,n-1) reached.
Code (Java)
// Imports Java utility classes for collections.
import java.util.*;
// Main class containing test cases for the solution.
public class Main {
// Entry point that runs test cases for the shortest path algorithm.
public static void main(String[] args) {
// Create an instance of Solution to call the method.
Solution sol = new Solution();
// Test case 1: 2x2 matrix with diagonal path
// Initialize a 2x2 grid with a diagonal clear path.
int[][] grid1 = {{0,1},{1,0}};
// Run test 1 and print result, expecting path length 2.
System.out.println("Test 1 (expect 2): " + sol.shortestPathBinaryMatrix(grid1));
// Test case 2: 1x1 matrix with 0
// Initialize a 1x1 grid with a single clear cell.
int[][] grid2 = {{0}};
// Run test 2 and print result, expecting 1.
System.out.println("Test 2 (expect 1): " + sol.shortestPathBinaryMatrix(grid2));
// Test case 3: 1x1 matrix with 1 (blocked)
// Initialize a 1x1 grid with a blocked cell.
int[][] grid3 = {{1}};
// Run test 3 and print result, expecting -1.
System.out.println("Test 3 (expect -1): " + sol.shortestPathBinaryMatrix(grid3));
// Test case 4: 3x3 clear path
// Initialize a 3x3 grid with all cells clear.
int[][] grid4 = {{0,0,0},{0,0,0},{0,0,0}};
// Run test 4 and print result, expecting 3.
System.out.println("Test 4 (expect 3): " + sol.shortestPathBinaryMatrix(grid4));
// Test case 5: blocked start or end
// Initialize a 2x2 grid with blocked start cell.
int[][] grid5 = {{1,0},{0,0}};
// Run test 5 and print result, expecting -1.
System.out.println("Test 5 (expect -1): " + sol.shortestPathBinaryMatrix(grid5));
// End of main method.
}
// End of Main class.
}
// Solution class containing the shortest path algorithm.
class Solution {
// Method to find shortest clear path in binary matrix using BFS; returns -1 if start or end blocked, 1 for single cell.
public int shortestPathBinaryMatrix(int[][] g){int n=g.length;if(g[0][0]==1||g[n-1][n-1]==1)return -1;if(n==1)return 1;
// Define 8-directional moves, initialize BFS queue with starting cell (0,0) and distance 1, mark start as visited.
int[][] dirs={{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}};Deque<int[]> q=new ArrayDeque<>();q.add(new int[]{0,0,1});g[0][0]=1;
// BFS loop: process each cell, explore 8 neighbors, if neighbor is target return distance+1, else mark visited and enqueue with incremented distance; return -1 if no path.
while(!q.isEmpty()){int[] c=q.poll();int r=c[0],co=c[1],d=c[2];for(int[] dir:dirs){int nr=r+dir[0],nc=co+dir[1];if(nr>=0&&nc>=0&&nr<n&&nc<n&&g[nr][nc]==0){if(nr==n-1&&nc==n-1)return d+1;g[nr][nc]=1;q.add(new int[]{nr,nc,d+1});}}}return -1;}
// Additional main method for quick test (duplicate of test 1).
public static void main(String[] args){System.out.println(new Solution().shortestPathBinaryMatrix(new int[][]{{0,1},{1,0}}));}
// End of Solution class.
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D117. Pacific Atlantic Water Flow
O(mn)Dual ocean DFSJava + JS lab
Cells that can flow to both Pacific and Atlantic.
Approach
DFS uphill from both ocean borders; return intersection.
Easy Algorithm
mark Pacific-reachable
mark Atlantic-reachable
intersect
Code (Java)
import java.util.*;
// Main class to demonstrate the Pacific Atlantic Water Flow solution
public class Main {
public static void main(String[] args) {
// Create solution instance
Solution solution = new Solution();
// Test case 1: simple 2x2 grid
int[][] heights1 = {{1,2},{4,3}};
System.out.println("Test 1: " + solution.pacificAtlantic(heights1));
// Test case 2: larger grid
int[][] heights2 = {{1,2,2,3,5},{3,2,3,4,4},{2,4,5,3,1},{6,7,1,4,5},{5,1,1,2,4}};
System.out.println("Test 2: " + solution.pacificAtlantic(heights2));
}
}
// Solution class implementing Pacific Atlantic Water Flow using DFS from both oceans
class Solution { // Solution class for water flow problem
public List<List<Integer>> pacificAtlantic(int[][] h){int R=h.length,C=h[0].length;boolean[][] p=new boolean[R][C],a=new boolean[R][C]; // Initialize dimensions and visited arrays for Pacific (p) and Atlantic (a)
for(int r=0;r<R;r++){dfs(h,p,r,0);dfs(h,a,r,C-1);}for(int c=0;c<C;c++){dfs(h,p,0,c);dfs(h,a,R-1,c);} // Start DFS from all border cells touching each ocean
List<List<Integer>> res=new ArrayList<>();for(int r=0;r<R;r++)for(int c=0;c<C;c++)if(p[r][c]&&a[r][c])res.add(List.of(r,c));return res;} // Collect cells reachable from both oceans
void dfs(int[][] h,boolean[][] seen,int r,int c){if(seen[r][c])return;seen[r][c]=true;int[][] d={{1,0},{-1,0},{0,1},{0,-1}}; // DFS with four direction vectors
for(int[] x:d){int nr=r+x[0],nc=c+x[1];if(nr>=0&&nc>=0&&nr<h.length&&nc<h[0].length&&h[nr][nc]>=h[r][c])dfs(h,seen,nr,nc);}} // Recurse to neighbors with equal or higher height
public static void main(String[] args){System.out.println(new Solution().pacificAtlantic(new int[][]{{1,2},{4,3}}));} // Snippet's own demo main
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D118. Find the Town Judge
O(n)Degree scoreJava + JS lab
Judge trusted by everyone, trusts nobody.
Approach
delta[i]+=1 when trusted, -1 when trusts; judge has n-1.
Easy Algorithm
apply trust deltas
find delta==n-1
Code (Java)
import java.util.*;
// Main class to demonstrate the findJudge solution
public class Main {
// Entry point: runs sample test cases for the town judge finder
public static void main(String[] args) {
Solution sol = new Solution(); // Create solution instance
// Test case 1: 3 people, 1 and 2 trust 3 -> judge is 3
System.out.println("Test 1 (expect 3): " + sol.findJudge(3, new int[][]{{1,3},{2,3}}));
// Test case 2: 4 people, no judge
System.out.println("Test 2 (expect -1): " + sol.findJudge(4, new int[][]{{1,3},{2,3},{3,1}}));
// Test case 3: 1 person, no trust -> that person is judge
System.out.println("Test 3 (expect 1): " + sol.findJudge(1, new int[][]{}));
}
}
// Solution class implementing the town judge algorithm using in-degree minus out-degree
class Solution { // Solution class from snippet
public int findJudge(int n,int[][] trust){int[] d=new int[n+1];for(int[] t:trust){d[t[0]]--;d[t[1]]++;}for(int i=1;i<=n;i++)if(d[i]==n-1)return i;return -1;} // Finds judge by trust score (in-degree minus out-degree)
public static void main(String[] args){System.out.println(new Solution().findJudge(3,new int[][]{{1,3},{2,3}}));} // Original snippet demo
} // End of Solution class
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D119. Rotate Image
O(n²)Transpose + reverseJava + JS lab
Rotate n×n matrix 90° clockwise in-place.
Approach
Transpose then reverse each row.
Easy Algorithm
transpose
reverse rows
Code (Java)
import java.util.*;
/* Main class: entry point for the program, demonstrates matrix rotation */
public class Main {
public static void main(String[] args) {
// Demo the rotate method with a 3x3 matrix
int[][] matrix = {{1,2,3},{4,5,6},{7,8,9}};
Solution sol = new Solution();
int[][] rotated = sol.rotate(matrix);
System.out.println(Arrays.deepToString(rotated));
}
}
/* Solution class: provides a method to rotate a square matrix 90 degrees clockwise in-place */
class Solution {
public int[][] rotate(int[][] m){int n=m.length;for(int i=0;i<n;i++)for(int j=i+1;j<n;j++){int t=m[i][j];m[i][j]=m[j][i];m[j][i]=t;} // Transpose the matrix
for(int[] row:m)for(int l=0,r=n-1;l<r;l++,r--){int t=row[l];row[l]=row[r];row[r]=t;}return m;} // Reverse each row
public static void main(String[] args){System.out.println(Arrays.deepToString(new Solution().rotate(new int[][]{{1,2,3},{4,5,6},{7,8,9}})));} // Demo main
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D120. Set Matrix Zeroes
O(mn)/O(1)Border markersJava + JS lab
If cell is 0, zero its row and column in-place.
Approach
Use first row/col as markers; second pass apply.
Easy Algorithm
mark with first row/col
apply markers
zero borders if needed
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
int[][] matrix = {{1,1,1},{1,0,1},{1,1,1}};
Solution sol = new Solution();
int[][] result = sol.setZeroes(matrix);
System.out.println(Arrays.deepToString(result));
}
}
class Solution {
public int[][] setZeroes(int[][] m){int R=m.length,C=m[0].length;boolean fr=false,fc=false; // initialize dimensions and flags
for(int c=0;c<C;c++)if(m[0][c]==0)fr=true;for(int r=0;r<R;r++)if(m[r][0]==0)fc=true; // check first row and column for zeros
for(int r=1;r<R;r++)for(int c=1;c<C;c++)if(m[r][c]==0){m[r][0]=0;m[0][c]=0;} // mark rows and columns using first row/col
for(int r=1;r<R;r++)for(int c=1;c<C;c++)if(m[r][0]==0||m[0][c]==0)m[r][c]=0; // zero cells based on marks
if(fr)for(int c=0;c<C;c++)m[0][c]=0;if(fc)for(int r=0;r<R;r++)m[r][0]=0;return m;} // zero first row/col if needed
public static void main(String[] args){System.out.println(Arrays.deepToString(new Solution().setZeroes(new int[][]{{1,1,1},{1,0,1},{1,1,1}})));} // demo main
} // end class
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D121. Valid Sudoku
O(1)Row/col/box setsJava + JS lab
Check 9×9 board has no duplicate digits in row/col/box.
Approach
Boolean maps for rows, cols, boxes (r/3*3+c/3).
Easy Algorithm
skip dots
box index
reject dups
Code (Java)
// Import Java utility classes for collections.
import java.util.*;
// Define the Main class containing the demonstration entry point.
public class Main {
// Main method starts; args unused.
public static void main(String[] args) {
// valid board
// Array of strings representing a valid Sudoku board rows.
String[] validRows = {
"53..7....",
"6..195...",
".98....6.",
"8...6...3",
"4..8.3..1",
"7...2...6",
".6....28.",
"...419..5",
"....8..79"
};
// Create 9x9 char array to hold the valid board.
char[][] validBoard = new char[9][9];
// Loop over each row index.
for (int i = 0; i < 9; i++) {
// Convert each string row to char array and store.
validBoard[i] = validRows[i].toCharArray();
}
// invalid board: duplicate '5' in first row
String[] invalidRows = {
"55..7....",
"6..195...",
".98....6.",
"8...6...3",
"4..8.3..1",
"7...2...6",
".6....28.",
"...419..5",
"....8..79"
};
// Create 9x9 char array for the invalid board.
char[][] invalidBoard = new char[9][9];
// Loop over each row index.
for (int i = 0; i < 9; i++) {
// Convert each string row to char array and store.
invalidBoard[i] = invalidRows[i].toCharArray();
}
// Instantiate Solution to validate boards.
Solution solver = new Solution();
// Print validation result for the valid board.
System.out.println("Valid board: " + solver.isValidSudoku(validBoard));
// Print validation result for the invalid board.
System.out.println("Invalid board: " + solver.isValidSudoku(invalidBoard));
}
}
// Solution class containing Sudoku validation logic.
class Solution {
// isValidSudoku method: uses three boolean 9x9 arrays to track digits seen in rows, columns, and 3x3 boxes.
public boolean isValidSudoku(char[][] board){boolean[][] rows=new boolean[9][9],cols=new boolean[9][9],boxes=new boolean[9][9];
// Iterate every cell; skip '.'; compute digit index and box index; if digit already seen in row, column, or box return false; otherwise mark seen.
for(int r=0;r<9;r++)for(int c=0;c<9;c++){if(board[r][c]=='.')continue;int d=board[r][c]-'1';int b=(r/3)*3+c/3;if(rows[r][d]||cols[c][d]||boxes[b][d])return false;rows[r][d]=cols[c][d]=boxes[b][d]=true;}return true;}
// Return true if no duplicates found.
public static void main(String[] args){System.out.println("ok");}
// Unused main method in Solution class.
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D122. First Missing Positive
O(n)/O(1)Index as hashJava + JS lab
Smallest missing positive integer in unsorted array (O(n), O(1) extra).
Approach
Place each value v at index v-1 via swaps; scan for first mismatch.
Easy Algorithm
swap nums[i] into place while in range
scan for nums[i]!=i+1
else return n+1
Code (Java)
import java.util.*;
public class Main {
// Solution class implementing the first missing positive algorithm via cyclic sort
static class Solution {
// Finds the smallest missing positive integer by placing each value v at index v-1
public int firstMissingPositive(int[] nums){int n=nums.length;for(int i=0;i<n;i++)while(nums[i]>0&&nums[i]<=n&&nums[nums[i]-1]!=nums[i]){int j=nums[i]-1;int t=nums[i];nums[i]=nums[j];nums[j]=t;} // cyclic swap to put nums[i] at its correct position
for(int i=0;i<n;i++)if(nums[i]!=i+1)return i+1;return n+1;} // scan for first index where value != index+1
// Original main method from the snippet (kept verbatim)
public static void main(String[] args){System.out.println(new Solution().firstMissingPositive(new int[]{3,4,-1,1}));}
}
// Main entry point demonstrating the solution with multiple test cases
public static void main(String[] args) {
Solution sol = new Solution();
// Test case 1: Example from the snippet
int[] test1 = {3, 4, -1, 1};
System.out.println("Test 1: " + sol.firstMissingPositive(test1)); // Expected: 2
// Test case 2: Already sequential positive integers
int[] test2 = {1, 2, 3};
System.out.println("Test 2: " + sol.firstMissingPositive(test2)); // Expected: 4
// Test case 3: Missing at the start
int[] test3 = {2, 3, 4};
System.out.println("Test 3: " + sol.firstMissingPositive(test3)); // Expected: 1
// Test case 4: With duplicates and non-positive numbers
int[] test4 = {1, 1, 0, -1, 2};
System.out.println("Test 4: " + sol.firstMissingPositive(test4)); // Expected: 3
// Test case 5: Empty array
int[] test5 = {};
System.out.println("Test 5: " + sol.firstMissingPositive(test5)); // Expected: 1
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
// Import Java utility classes like List, ArrayList, Arrays.
import java.util.*;
// Main class containing the entry point for the program.
public class Main {
// Main method: sets up test operations and runs the circular queue simulation.
public static void main(String[] args) {
// Define a list of operations to simulate the circular queue API.
List<List<Object>> ops = Arrays.asList(
// Create a circular queue with capacity 3.
Arrays.asList("MyCircularQueue", 3),
// Enqueue value 1.
Arrays.asList("enQueue", 1),
// Enqueue value 2.
Arrays.asList("enQueue", 2),
// Enqueue value 3 (queue becomes full).
Arrays.asList("enQueue", 3),
// Attempt to enqueue 4 (should fail, queue is full).
Arrays.asList("enQueue", 4),
// Get the rear element.
Arrays.asList("Rear"),
// Check if queue is full.
Arrays.asList("isFull"),
// Dequeue an element.
Arrays.asList("deQueue"),
// Enqueue 4 again (should succeed now).
Arrays.asList("enQueue", 4),
// Get the rear element again.
Arrays.asList("Rear")
);
// Instantiate the Solution class that contains the queue logic.
Solution sol = new Solution();
// Execute the sequence of operations and collect results.
List<Object> result = sol.runCircularQueue(ops);
// Print the list of results to console.
System.out.println(result);
}
}
// Solution class containing the circular queue implementation and runner.
class Solution {
// Inner class MyCircularQueue: array-backed circular queue with head index and size.
static class MyCircularQueue{int[] a;int head,size;
// Constructor allocates the internal array of given capacity k.
MyCircularQueue(int k){a=new int[k];}
// enQueue: inserts value at tail position (head+size)%capacity; returns false if full.
boolean enQueue(int v){if(isFull())return false;a[(head+size)%a.length]=v;size++;return true;}
// deQueue: advances head index modulo capacity and decrements size; returns false if empty.
boolean deQueue(){if(isEmpty())return false;head=(head+1)%a.length;size--;return true;}
// Front returns element at head; Rear returns element at tail (head+size-1)%capacity; both return -1 if empty.
int Front(){return isEmpty()?-1:a[head];} int Rear(){return isEmpty()?-1:a[(head+size-1)%a.length];}
// isEmpty checks size==0; isFull checks size==capacity.
boolean isEmpty(){return size==0;} boolean isFull(){return size==a.length;}}
// runCircularQueue: processes a list of operation calls and returns a list of results.
public List<Object> runCircularQueue(List<List<Object>> ops){MyCircularQueue q=null;List<Object> out=new ArrayList<>();
// Loop over each operation; on "MyCircularQueue" create new queue and add null to output.
for(List<Object> op:ops){String m=(String)op.get(0);if(m.equals("MyCircularQueue")){q=new MyCircularQueue((Integer)op.get(1));out.add(null);}
// Handle enQueue and deQueue operations, appending their boolean results.
else if(m.equals("enQueue"))out.add(q.enQueue((Integer)op.get(1)));else if(m.equals("deQueue"))out.add(q.deQueue());
// Handle Front and Rear queries, appending the returned integer values.
else if(m.equals("Front"))out.add(q.Front());else if(m.equals("Rear"))out.add(q.Rear());
// Handle isEmpty and isFull queries, appending boolean results; return the output list.
else if(m.equals("isEmpty"))out.add(q.isEmpty());else out.add(q.isFull());}return out;}
// Unused main method inside Solution (likely a leftover).
public static void main(String[] args){System.out.println("ok");}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D124. Time Based Key-Value Store
O(log n) getBinary search timestampsJava + JS lab
set(key,value,timestamp); get(key,timestamp) → latest value ≤ timestamp.
Approach
Map key→list of (ts,val); binary search on get.
Easy Algorithm
append on set (ts increasing)
bisect right for ≤ts
return value or empty
Code (Java)
// Import Java utility classes for collections like List, ArrayList, Arrays.
import java.util.*;
// Define the Main class containing the program entry point.
public class Main {
// Main method that builds sample operations and executes the TimeMap simulation.
public static void main(String[] args) {
// build sample ops
// Create a list of operations where each operation is a list of objects (method name + args).
List<List<Object>> ops = Arrays.asList(
// First operation: instantiate a new TimeMap.
Arrays.asList("TimeMap"),
// Second operation: set key 'foo' to value 'bar' at timestamp 1.
Arrays.asList("set", "foo", "bar", 1),
// Third operation: get value for key 'foo' at timestamp 1.
Arrays.asList("get", "foo", 1),
// Fourth operation: get value for key 'foo' at timestamp 3.
Arrays.asList("get", "foo", 3),
// Fifth operation: set key 'foo' to value 'bar2' at timestamp 4.
Arrays.asList("set", "foo", "bar2", 4),
// Sixth operation: get value for key 'foo' at timestamp 4.
Arrays.asList("get", "foo", 4),
// Seventh operation: get value for key 'foo' at timestamp 5.
Arrays.asList("get", "foo", 5)
);
// Create an instance of Solution to run the TimeMap operations.
Solution sol = new Solution();
// Execute the operations and collect results in a list.
List<Object> result = sol.runTimeMap(ops);
// Print the results to standard output.
System.out.println(result);
}
}
// snippet verbatim below
// Define the Solution class containing TimeMap implementation and runner.
class Solution {
// Define TimeMap inner class with a map storing key to list of int arrays (unused field).
static class TimeMap{Map<String,List<int[]>> m=new HashMap<>(); // store idx+string separately
// Two maps: vals stores key to list of string values, ts stores key to list of timestamps.
Map<String,List<String>> vals=new HashMap<>();Map<String,List<Integer>> ts=new HashMap<>();
// Set method: appends timestamp to ts map and value to vals map for the given key.
void set(String k,String v,int t){ts.computeIfAbsent(k,x->new ArrayList<>()).add(t);vals.computeIfAbsent(k,x->new ArrayList<>()).add(v);}
// Get method: binary searches timestamps list for largest timestamp <= target, returns corresponding value or empty string.
String get(String k,int t){List<Integer> a=ts.get(k);if(a==null)return "";int lo=0,hi=a.size()-1,ans=-1;while(lo<=hi){int mid=(lo+hi)/2;if(a.get(mid)<=t){ans=mid;lo=mid+1;}else hi=mid-1;}return ans<0?"":vals.get(k).get(ans);}}
// runTimeMap method: processes a list of operations, maintains TimeMap instance, returns list of results.
public List<Object> runTimeMap(List<List<Object>> ops){TimeMap tm=null;List<Object> out=new ArrayList<>();
// Iterate through operations; on 'TimeMap' create new instance and add null to output.
for(List<Object> op:ops){String m=(String)op.get(0);if(m.equals("TimeMap")){tm=new TimeMap();out.add(null);}
// On 'set' operation, call set with key, value, timestamp and add null to output.
else if(m.equals("set")){tm.set((String)op.get(1),(String)op.get(2),(Integer)op.get(3));out.add(null);}
// On 'get' operation, call get with key and timestamp, add returned value to output.
else out.add(tm.get((String)op.get(1),(Integer)op.get(2)));}return out;}
// Static main method for standalone testing, prints 'ok'.
public static void main(String[] args){System.out.println("ok");}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D125. Design Hit Counter
O(1) amortQueue of timestampsJava + JS lab
hit(t); getHits(t) = hits in past 300 seconds.
Approach
Queue of timestamps; evict < t-299 on getHits.
Easy Algorithm
enqueue on hit
evict old on query
return size
Code (Java)
import java.util.*;
public class Main {
// Main class to demonstrate the HitCounter solution
// Solution is nested as a static class so the single file stays valid on older Java versions
static class Solution {
// HitCounter uses a deque to store timestamps of hits
static class HitCounter{Deque<Integer> q=new ArrayDeque<>(); // stores hit timestamps
void hit(int t){q.addLast(t);} // record a hit at time t
int getHits(int t){while(!q.isEmpty()&&q.peekFirst()<=t-300)q.pollFirst();return q.size();}} // remove hits older than 300 seconds, return count
public List<Object> runHitCounter(List<List<Object>> ops){HitCounter h=null;List<Object> out=new ArrayList<>(); // process operations
for(List<Object> op:ops){String m=(String)op.get(0);if(m.equals("HitCounter")){h=new HitCounter();out.add(null);} // initialize HitCounter
else if(m.equals("hit")){h.hit((Integer)op.get(1));out.add(null);}else out.add(h.getHits((Integer)op.get(1)));}return out;} // handle hit or getHits
public static void main(String[] args){System.out.println("ok");} // snippet's own main (not used)
}
// Main entry point
public static void main(String[] args) {
// Create an instance of Solution
Solution sol = new Solution();
// Define a sequence of operations to test HitCounter
List<List<Object>> ops = new ArrayList<>();
ops.add(Arrays.asList("HitCounter"));
ops.add(Arrays.asList("hit", 1));
ops.add(Arrays.asList("hit", 2));
ops.add(Arrays.asList("hit", 3));
ops.add(Arrays.asList("getHits", 4));
ops.add(Arrays.asList("hit", 300));
ops.add(Arrays.asList("getHits", 300));
ops.add(Arrays.asList("getHits", 301));
// Run the operations and print results
List<Object> result = sol.runHitCounter(ops);
System.out.println("Result: " + result);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D126. Online Stock Span
O(1) amortMonotonic decreasing stackJava + JS lab
stockSpans: consecutive days price ≤ today including today.
Approach
Stack of (price,span); pop ≤ today's price accumulating spans.
Easy Algorithm
span=1
while top.price<=price: span+=top.span pop
push (price,span)
Code (Java)
import java.util.*;
public class Main {
// Solution is nested as a static class so the single file stays valid on older Java versions
static class Solution {
static class StockSpanner{Deque<int[]> st=new ArrayDeque<>(); // monotonic decreasing stack of (price, span)
int next(int price){int span=1;while(!st.isEmpty()&&st.peek()[0]<=price)span+=st.pop()[1];st.push(new int[]{price,span});return span;}} // compute span for price
public List<Object> runStockSpanner(List<List<Object>> ops){StockSpanner s=null;List<Object> out=new ArrayList<>(); // process operations
for(List<Object> op:ops){String m=(String)op.get(0);if(m.equals("StockSpanner")){s=new StockSpanner();out.add(null);}else out.add(s.next((Integer)op.get(1)));}return out;} // iterate ops
public static void main(String[] args){System.out.println("ok");} // demo main in Solution
}
public static void main(String[] args) {
// Create Solution instance
Solution sol = new Solution();
// Prepare sample operations: constructor then a series of next calls
List<List<Object>> ops = new ArrayList<>();
ops.add(List.of("StockSpanner"));
ops.add(List.of("next", 100));
ops.add(List.of("next", 80));
ops.add(List.of("next", 60));
ops.add(List.of("next", 70));
ops.add(List.of("next", 60));
ops.add(List.of("next", 75));
ops.add(List.of("next", 85));
// Run the stock spanner simulation
List<Object> result = sol.runStockSpanner(ops);
// Print the resulting spans (null for constructor, then spans)
System.out.println("Stock spans: " + result);
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D127. Daily Temperatures II / Next Greater Element I
O(n)Hash + mono stackJava + JS lab
nums1 subset of nums2; next greater of each nums1 element in nums2 else -1.
Approach
Mono decreasing stack on nums2 → map next greater; probe nums1.
Easy Algorithm
scan nums2 right/left with stack
map value→next greater
lookup nums1
Code (Java)
import java.util.*;
// Main class to demonstrate the Solution
public class Main {
public static void main(String[] args) {
// Create an instance of Solution
Solution sol = new Solution();
// Test case from the snippet
int[] nums1 = {4, 1, 2};
int[] nums2 = {1, 3, 4, 2};
int[] result = sol.nextGreaterElement(nums1, nums2);
// Print the result
System.out.println("Result: " + Arrays.toString(result));
// Also run the original main from Solution for verification
Solution.main(args);
}
}
// Solution class containing the nextGreaterElement algorithm
class Solution {
// Method to compute next greater elements for nums1 based on nums2
public int[] nextGreaterElement(int[] nums1,int[] nums2){Map<Integer,Integer> next=new HashMap<>();Deque<Integer> st=new ArrayDeque<>(); // initialize map and stack
// Iterate through nums2, maintaining a decreasing stack to find next greater
for(int x:nums2){while(!st.isEmpty()&&st.peek()<x)next.put(st.pop(),x);st.push(x);} // process each element
// Build result array by looking up each nums1 element in the map
int[] ans=new int[nums1.length];for(int i=0;i<nums1.length;i++)ans[i]=next.getOrDefault(nums1[i],-1);return ans;} // fill ans and return
// Original main method from the snippet for quick test
public static void main(String[] args){System.out.println(Arrays.toString(new Solution().nextGreaterElement(new int[]{4,1,2},new int[]{1,3,4,2})));} // print test result
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D128. Top K Frequent Words
O(n log k)Heap + alpha tieJava + JS lab
k most frequent words; ties: lexicographically smaller first.
Approach
Count map; min-heap of size k with custom comparator (freq, then reverse alpha).
Easy Algorithm
count words
heap keep top k
extract sorted
Code (Java)
import java.util.*;
// Main class to run the solution
public class Main {
public static void main(String[] args) {
// Create Solution instance and call topKFrequent with sample input
Solution sol = new Solution();
List<String> result = sol.topKFrequent(new String[]{"i","love","leetcode","i","love","coding"}, 2);
// Print the result
System.out.println(result);
}
}
// Solution class implementing top K frequent words algorithm
class Solution {
// Count word frequencies using HashMap
public List<String> topKFrequent(String[] words,int k){Map<String,Integer> cnt=new HashMap<>();for(String w:words)cnt.merge(w,1,Integer::sum);
// Create min-heap with custom comparator: first by frequency (ascending), then by reverse lexicographical order
PriorityQueue<String> pq=new PriorityQueue<>((a,b)->{int c=cnt.get(a)-cnt.get(b);return c!=0?c:b.compareTo(a);});
// Add all words to heap, keeping only top k by polling when size exceeds k
for(String w:cnt.keySet()){pq.offer(w);if(pq.size()>k)pq.poll();}List<String> res=new ArrayList<>();while(!pq.isEmpty())res.add(pq.poll());Collections.reverse(res);return res;}
// Original main method from snippet (kept for reference)
public static void main(String[] args){System.out.println(new Solution().topKFrequent(new String[]{"i","love","leetcode","i","love","coding"},2));}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D129. Meeting Rooms
O(n log n)Sort by startJava + JS lab
Return true if person can attend all meetings (no overlap).
Approach
Sort by start; if intervals[i].start < intervals[i-1].end → false.
Easy Algorithm
sort by start
check adjacent overlap
Code (Java)
import java.util.*;
/**
* Main class to demonstrate the meeting rooms solution.
*/
public class Main {
public static void main(String[] args) {
Solution sol = new Solution(); // create instance
// Test case 1: overlapping meetings
int[][] intervals1 = {{0,30},{5,10},{15,20}};
System.out.println("Test 1 (overlap): " + sol.canAttendMeetings(intervals1)); // expected false
// Test case 2: non-overlapping meetings
int[][] intervals2 = {{7,10},{2,4}};
System.out.println("Test 2 (no overlap): " + sol.canAttendMeetings(intervals2)); // expected true
// Test case 3: empty intervals
int[][] intervals3 = {};
System.out.println("Test 3 (empty): " + sol.canAttendMeetings(intervals3)); // expected true
// Test case 4: single meeting
int[][] intervals4 = {{5,10}};
System.out.println("Test 4 (single): " + sol.canAttendMeetings(intervals4)); // expected true
}
}
/**
* Solution class that checks if a person can attend all meetings without overlap.
*/
class Solution {
public boolean canAttendMeetings(int[][] intervals){Arrays.sort(intervals,Comparator.comparingInt(a->a[0])); // sort intervals by start time
for(int i=1;i<intervals.length;i++)if(intervals[i][0]<intervals[i-1][1])return false;return true;} // check adjacent intervals for overlap
public static void main(String[] args){System.out.println(new Solution().canAttendMeetings(new int[][]{{0,30},{5,10},{15,20}}));} // original main for demonstration
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D130. Insert Delete GetRandom O(1)
O(1) avgArray + HashMapJava + JS lab
RandomizedSet: insert/remove/getRandom average O(1).
Approach
Array for values; map val→index; swap-remove last on delete.
Easy Algorithm
insert if absent append
remove swap with last
getRandom index
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
Solution sol = new Solution();
// Build a sequence of operations to exercise RandomizedSet
List<List<Object>> ops = new ArrayList<>();
ops.add(Arrays.asList("RandomizedSet")); // constructor call
ops.add(Arrays.asList("insert", 10)); // insert 10 -> true
ops.add(Arrays.asList("insert", 20)); // insert 20 -> true
ops.add(Arrays.asList("insert", 10)); // insert duplicate 10 -> false
ops.add(Arrays.asList("remove", 20)); // remove 20 -> true
ops.add(Arrays.asList("remove", 30)); // remove absent 30 -> false
ops.add(Arrays.asList("getRandom")); // random from {10}
ops.add(Arrays.asList("getRandom")); // random from {10}
ops.add(Arrays.asList("getRandom")); // random from {10}
List<Object> results = sol.runRandomizedSet(ops);
// Print each result so the reader sees the output
for (Object result : results) {
System.out.println(result);
}
}
}
// Solution class containing RandomizedSet (O(1) insert/remove/getRandom) and test runner
class Solution {
// RandomizedSet: ArrayList stores values, HashMap maps value->index, fixed-seed Random for determinism
static class RandomizedSet{List<Integer> a=new ArrayList<>();Map<Integer,Integer> idx=new HashMap<>();Random r=new Random(0);
// insert: if value exists return false; else append to list, record index, return true
boolean insert(int v){if(idx.containsKey(v))return false;idx.put(v,a.size());a.add(v);return true;}
// remove: if value absent return false; else swap with last, pop last, update map, return true
boolean remove(int v){if(!idx.containsKey(v))return false;int i=idx.get(v),last=a.get(a.size()-1);a.set(i,last);idx.put(last,i);a.remove(a.size()-1);idx.remove(v);return true;}
// getRandom: pick random index from list and return that value
int getRandom(){return a.get(r.nextInt(a.size()));}}
// runRandomizedSet: interprets a list of operations, returns list of results (null for constructor)
public List<Object> runRandomizedSet(List<List<Object>> ops){RandomizedSet s=null;List<Object> out=new ArrayList<>();
for(List<Object> op:ops){String m=(String)op.get(0);if(m.equals("RandomizedSet")){s=new RandomizedSet();out.add(null);}
else if(m.equals("insert"))out.add(s.insert((Integer)op.get(1)));else if(m.equals("remove"))out.add(s.remove((Integer)op.get(1)));
else out.add(s.getRandom());}return out;}
// Original main from snippet (not used when running Main)
public static void main(String[] args){System.out.println("ok");}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
import java.util.*;
// Main class demonstrating the RPN evaluator
public class Main {
public static void main(String[] args) {
Solution sol = new Solution();
// Test 1: (2 + 1) * 3 = 9
String[] tokens1 = {"2", "1", "+", "3", "*"};
System.out.println("Test 1: " + sol.evalRPN(tokens1));
// Test 2: 4 + (13 * 5) = 69
String[] tokens2 = {"4", "13", "5", "*", "+"};
System.out.println("Test 2: " + sol.evalRPN(tokens2));
// Test 3: (10 - 6) * 2 = 8
String[] tokens3 = {"10", "6", "-", "2", "*"};
System.out.println("Test 3: " + sol.evalRPN(tokens3));
}
}
// Solution class implementing Reverse Polish Notation evaluation
class Solution {
// Evaluates RPN expression using a stack
public int evalRPN(String[] tokens){Deque<Integer> st=new ArrayDeque<>();
// Iterate through each token
for(String t:tokens){if(t.equals("+")||t.equals("-")||t.equals("*")||t.equals("/")){int b=st.pop(),a=st.pop();
// Apply operator: pop two operands, compute, push result
if(t.equals("+"))st.push(a+b);else if(t.equals("-"))st.push(a-b);else if(t.equals("*"))st.push(a*b);else st.push(a/b);}else st.push(Integer.parseInt(t));}
// Final result is the only element left on stack
return st.pop();}
// Original main method from snippet (kept as-is)
public static void main(String[] args){System.out.println(new Solution().evalRPN(new String[]{"2","1","+","3","*"}));}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D132. Basic Calculator II
O(n)Sign accumulatorJava + JS lab
Evaluate + - * / expression without parentheses (ints).
Approach
Scan numbers; on * / apply immediately to last; on + - push signed term; sum stack.
Easy Algorithm
parse number
on op flush previous
* / update last; + - push
Code (Java)
import java.util.*;
public class Main {
public static void main(String[] args) {
Solution solution = new Solution();
// Test the calculator with various expressions
System.out.println("3+2*2 = " + solution.calculate("3+2*2"));
System.out.println(" 3/2 = " + solution.calculate(" 3/2 "));
System.out.println("3+5 / 2 = " + solution.calculate("3+5 / 2"));
System.out.println("1+2*3-4/2 = " + solution.calculate("1+2*3-4/2"));
}
}
class Solution {
// Evaluates a basic arithmetic expression containing +, -, *, / and spaces
public int calculate(String s){Deque<Integer> st=new ArrayDeque<>();int num=0;char sign='+';
// Iterate through each character of the input string
for(int i=0;i<s.length();i++){char c=s.charAt(i);if(Character.isDigit(c))num=num*10+c-'0';
// When operator or end of string is reached, process the accumulated number with previous sign
if((!Character.isDigit(c)&&c!=' ')||i==s.length()-1){if(sign=='+')st.push(num);else if(sign=='-')st.push(-num);
// For * and /, immediately apply to the last value in stack (higher precedence)
else if(sign=='*')st.push(st.pop()*num);else st.push(st.pop()/num);sign=c;num=0;}}
// Sum all values in stack to produce final result
int sum=0;for(int x:st)sum+=x;return sum;}
// Demo main method included in original snippet
public static void main(String[] args){System.out.println(new Solution().calculate("3+2*2"));}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D133. LRU Cache Ops Runner
O(1)HashMap + DLLJava + JS lab
Capacity-bounded LRU: get/put O(1).
Approach
HashMap to nodes; doubly linked list for recency; move-to-front on access.
Easy Algorithm
get: move front or -1
put: update/move or insert
evict tail if over cap
Code (Java)
// Import standard Java utilities including List, Map, HashMap, Arrays, ArrayList.
import java.util.*;
/** Main class: runs a short LRUCache scenario through Solution.runLRU and prints the results. */
public class Main {
public static void main(String[] args) {
// Capacity 2: put(1,1), put(2,2), get(1) -> 1, put(3,3) evicts key 2, get(2) -> -1
List<List<Object>> ops = Arrays.asList(
Arrays.asList("LRUCache", 2),
Arrays.asList("put", 1, 1),
Arrays.asList("put", 2, 2),
Arrays.asList("get", 1),
Arrays.asList("put", 3, 3),
Arrays.asList("get", 2));
System.out.println(new Solution().runLRU(ops)); // [null, null, null, 1, null, -1]
}
}
// Solution class containing the LRUCache implementation and a driver to execute operations.
class Solution {
// Inner class LRUCache implements a Least Recently Used cache with O(1) get and put.
static class LRUCache{
// Static nested Node class for doubly linked list; stores key, value, prev, next pointers.
static class N{int k,v;N p,n;N(int k,int v){this.k=k;this.v=v;}}
// Cache fields: capacity, hashmap for O(1) key lookup, dummy head/tail nodes to simplify list ops.
int cap;Map<Integer,N> m=new HashMap<>();N head=new N(0,0),tail=new N(0,0);
// Constructor sets capacity and links dummy head and tail to each other (empty list).
LRUCache(int c){cap=c;head.n=tail;tail.p=head;}
// add inserts a node right after head, making it the most recently used.
void add(N x){x.n=head.n;x.p=head;head.n.p=x;head.n=x;}
// rem unlinks a node from the doubly linked list.
void rem(N x){x.p.n=x.n;x.n.p=x.p;}
// get returns value for key if present, moves accessed node to front (MRU), else -1.
int get(int k){N x=m.get(k);if(x==null)return -1;rem(x);add(x);return x.v;}
// put updates existing key or inserts new node; evicts LRU (tail.prev) when over capacity.
void put(int k,int v){N x=m.get(k);if(x!=null){x.v=v;rem(x);add(x);return;}x=new N(k,v);m.put(k,x);add(x);if(m.size()>cap){N lru=tail.p;rem(lru);m.remove(lru.k);}}}
// runLRU interprets a list of operation lists, executes them on an LRUCache, collects outputs.
public List<Object> runLRU(List<List<Object>> ops){LRUCache c=null;List<Object> out=new ArrayList<>();
// Iterates operations; on "LRUCache" creates new cache with given capacity, records null.
for(List<Object> op:ops){String m=(String)op.get(0);if(m.equals("LRUCache")){c=new LRUCache((Integer)op.get(1));out.add(null);}
// On "put" calls put and records null; on "get" calls get and records returned value.
else if(m.equals("put")){c.put((Integer)op.get(1),(Integer)op.get(2));out.add(null);}else out.add(c.get((Integer)op.get(1)));}return out;}
public static void main(String[] args){System.out.println("ok");}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D134. Sliding Window Median helper / Find Median from Data Stream
O(log n)Dual heapsJava + JS lab
MedianFinder: addNum + findMedian (online).
Approach
Max-heap lo + min-heap hi; rebalance sizes; median from tops.
Easy Algorithm
push to lo or hi by value
rebalance |lo-hi|≤1
median mid or avg
Code (Java)
// Import Java utility classes for collections and priority queues.
import java.util.*;
/** Main class: runs a short MedianFinder scenario through Solution.runMedianFinder and prints the results. */
public class Main {
public static void main(String[] args) {
// addNum(1), addNum(2) -> median 1.5; addNum(3) -> median 2.0
List<List<Object>> ops = Arrays.asList(
Arrays.asList("MedianFinder"),
Arrays.asList("addNum", 1),
Arrays.asList("addNum", 2),
Arrays.asList("findMedian"),
Arrays.asList("addNum", 3),
Arrays.asList("findMedian"));
System.out.println(new Solution().runMedianFinder(ops)); // [null, null, null, 1.5, null, 2.0]
}
}
// Solution class definition.
class Solution {
// MedianFinder inner class using two heaps: 'lo' (max-heap) stores the lower half, 'hi' (min-heap) stores the upper half.
static class MedianFinder{PriorityQueue<Integer> lo=new PriorityQueue<>(Collections.reverseOrder());PriorityQueue<Integer> hi=new PriorityQueue<>();
// addNum inserts a number into the appropriate heap then rebalances so sizes differ by at most one.
void addNum(int n){if(lo.isEmpty()||n<=lo.peek())lo.offer(n);else hi.offer(n);if(lo.size()>hi.size()+1)hi.offer(lo.poll());else if(hi.size()>lo.size())lo.offer(hi.poll());}
// findMedian returns the top of the larger heap, or the average of both tops when sizes are equal.
double findMedian(){if(lo.size()>hi.size())return lo.peek();return (lo.peek()+hi.peek())/2.0;}}
// runMedianFinder executes a list of operations (constructor, addNum, findMedian) and collects outputs.
public List<Object> runMedianFinder(List<List<Object>> ops){MedianFinder m=null;List<Object> out=new ArrayList<>();
// Iterates over operations; on 'MedianFinder' creates a new instance and records null output.
for(List<Object> op:ops){String s=(String)op.get(0);if(s.equals("MedianFinder")){m=new MedianFinder();out.add(null);}
// On 'addNum' delegates to MedianFinder and records null; otherwise calls findMedian and records the median.
else if(s.equals("addNum")){m.addNum((Integer)op.get(1));out.add(null);}else out.add(m.findMedian());}return out;}
// Secondary main method (unused in this test) that simply prints 'ok'.
public static void main(String[] args){System.out.println("ok");}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
See lab tests / Run main.
Also asked: Amazon, Target
D135. Minimum Cost For Tickets
Job Prep DPJava + JS labNew
Travel days + costs for 1/7/30-day passes; minimize cost to cover all days.
Approach
At each travel day index, choose buy 1/7/30 and jump to the next uncovered day; memoize on index.
Easy Algorithm
If i ≥ n return 0
Option 1-day: costs[0] + dp(i+1)
Find first day ≥ days[i]+7 / +30 for other options
Return min; memoize
Code (Java)
import java.util.*;
// Main class to execute the solution and demonstrate its usage
public class Main {
public static void main(String[] args) {
// Create an instance of the Solution class
Solution solution = new Solution();
// Test case 1: Example from the problem description
int[] days1 = {1, 4, 6, 7, 8, 20};
int[] costs1 = {2, 7, 15};
int result1 = solution.mincostTickets(days1, costs1);
System.out.println("Test case 1 - Minimum cost: " + result1);
// Test case 2: Additional test with more travel days
int[] days2 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 30, 31};
int[] costs2 = {2, 7, 15};
int result2 = solution.mincostTickets(days2, costs2);
System.out.println("Test case 2 - Minimum cost: " + result2);
}
}
// Solution class implementing the minimum cost for tickets algorithm using DP with memoization
class Solution {
// Public method to calculate minimum cost for tickets given travel days and pass costs
public int mincostTickets(int[] days, int[] costs) {
int[] memo = new int[days.length]; // Memoization array initialized to -1
Arrays.fill(memo, -1); // Fill memo with -1 to indicate uncomputed states
return dp(days, costs, 0, memo); // Start DP from day index 0
}
// Recursive DP helper with memoization: returns min cost from index i onwards
int dp(int[] days, int[] costs, int i, int[] memo) {
if (i >= days.length) return 0; // Base case: no more travel days, cost is 0
if (memo[i] >= 0) return memo[i]; // Return cached result if already computed
int best = costs[0] + dp(days, costs, i + 1, memo); // Option 1: buy 1-day pass for days[i]
int j = i; // Find first day not covered by 7-day pass
while (j < days.length && days[j] < days[i] + 7) j++;
best = Math.min(best, costs[1] + dp(days, costs, j, memo)); // Option 2: 7-day pass
j = i; // Find first day not covered by 30-day pass
while (j < days.length && days[j] < days[i] + 30) j++;
best = Math.min(best, costs[2] + dp(days, costs, j, memo)); // Option 3: 30-day pass
return memo[i] = best; // Cache and return minimum cost
}
// Original main method from the snippet for standalone testing
public static void main(String[] args) {
System.out.println(new Solution().mincostTickets(new int[]{1,4,6,7,8,20}, new int[]{2,7,15}));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
Sample [1,4,6,7,8,20]/[2,7,15] → 11. See lab tests.
Also asked: Job Prep DP · Amazon, Target
D136. 0/1 Knapsack
Job Prep DPJava + JS labNew
Maximize value with each item at most once under weight capacity.
Approach
Include/exclude recursion on (i, remainingWeight); memoize — core DP definition from Job Prep.
Easy Algorithm
Base: i==n or w==0 → 0
Skip = dp(i+1,w)
Take if weight fits
Memoize max
Code (Java)
import java.util.*; // Import utilities for Arrays.fill
/**
* Main entry point that demonstrates the 0/1 Knapsack solution.
* Creates a Solution instance and runs a sample test case.
*/
public class Main {
public static void main(String[] args) {
// Test the Solution class with sample data
Solution sol = new Solution();
int[] weights = {2, 3, 4, 5};
int[] values = {3, 4, 5, 6};
int capacity = 5;
int result = sol.knapsack(weights, values, capacity);
System.out.println("Max value: " + result);
}
}
/**
* Solution for 0/1 Knapsack problem using top-down DP with memoization.
* Each item can be taken at most once to maximize value under weight capacity.
*/
class Solution { // Solution class definition
public int knapsack(int[] weights, int[] values, int capacity) { // Knapsack entry method
int n = weights.length; // Number of items
int[][] memo = new int[n][capacity + 1]; // Memoization table: [item index][remaining weight]
for (int[] row : memo) Arrays.fill(row, -1); // Initialize memo with -1 (uncomputed)
return dfs(0, capacity, weights, values, memo); // Start recursion from first item with full capacity
} // End knapsack
int dfs(int i, int w, int[] wt, int[] val, int[][] memo) { // Recursive DFS with memo
if (i == wt.length || w == 0) return 0; // Base case: no items left or no capacity
if (memo[i][w] >= 0) return memo[i][w]; // Return cached result if available
int best = dfs(i + 1, w, wt, val, memo); // Option 1: skip current item
if (wt[i] <= w) best = Math.max(best, val[i] + dfs(i + 1, w - wt[i], wt, val, memo)); // Option 2: take current item if it fits
return memo[i][w] = best; // Cache and return best value
} // End dfs
public static void main(String[] args) { // Original test main in Solution
System.out.println(new Solution().knapsack(new int[]{1,2,3}, new int[]{6,10,12}, 5)); // Run sample test
} // End Solution.main
} // End Solution class
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
wt[1,2,3] val[6,10,12] W=5 → 22. See lab tests.
Also asked: Job Prep DP · Amazon, Target
D137. Best Time to Buy and Sell Stock III
Job Prep DPJava + JS labNew
At most two buy-sell transactions; maximize profit.
Approach
One-pass state machine: b1/s1/b2/s2.
Easy Algorithm
Init buys to −∞, sells to 0
Update first buy/sell
Update second buy/sell
Return s2
Code (Java)
import java.util.*;
/** Main class: runs the two-transaction stock profit example and prints the result. */
public class Main {
public static void main(String[] args) {
// Prices 3,3,5,0,0,3,1,4: buy at 0 sell at 3, then buy at 1 sell at 4 -> profit 3 + 3 = 6
System.out.println(new Solution().maxProfit(new int[]{3,3,5,0,0,3,1,4})); // 6
}
}
/** Solution class: best profit with at most two buy/sell transactions. */
class Solution {
// Method to calculate maximum profit with at most two transactions
public int maxProfit(int[] prices) {
// Initialize four state variables: b1/b2 track best buy prices, s1/s2 track best sell profits
int b1 = Integer.MIN_VALUE / 4, b2 = Integer.MIN_VALUE / 4, s1 = 0, s2 = 0;
// Iterate through each day's stock price
for (int p : prices) {
// Update first buy: either keep previous best or buy today (negative price)
b1 = Math.max(b1, -p);
// Update first sell: either keep previous best or sell today using best first buy
s1 = Math.max(s1, b1 + p);
// Update second buy: either keep previous best or buy today using profit from first sell
b2 = Math.max(b2, s1 - p);
// Update second sell: either keep previous best or sell today using best second buy
s2 = Math.max(s2, b2 + p);
}
// Return maximum profit after at most two transactions
return s2;
}
// Second main method for standalone testing of Solution class
public static void main(String[] args) {
// Run the same test case and print result
System.out.println(new Solution().maxProfit(new int[]{3,3,5,0,0,3,1,4}));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[3,3,5,0,0,3,1,4] → 6. See lab tests.
Also asked: Job Prep DP · Amazon, Target
D138. Paint House
Job Prep DPJava + JS labNew
Paint houses with 3 colors; adjacent houses differ; minimize cost.
Approach
In-place row DP: each color adds min of other two from previous house.
Easy Algorithm
For i=1..n-1 update each color
Add min of other colors from i-1
Return min of last row
Code (Java)
import java.util.*;
// Main class: entry point that demonstrates the Paint House DP solution
public class Main {
public static void main(String[] args) {
// Create an instance of the Solution class
Solution solver = new Solution();
// Example costs matrix: 3 houses, 3 colors each
int[][] costs = {
{17, 2, 17},
{16, 16, 5},
{14, 3, 19}
};
// Compute and print minimum painting cost
int result = solver.minCost(costs);
System.out.println("Minimum cost: " + result);
// Additional test case from the snippet's own main
System.out.println("Snippet's test result: " + solver.minCost(new int[][]{{17,2,17},{16,16,5},{14,3,19}}));
}
}
// Solution class: contains the minCost method for Paint House problem (in-place DP)
class Solution {
public int minCost(int[][] costs) {
if (costs == null || costs.length == 0) return 0; // Handle empty input
int n = costs.length; // Number of houses
for (int i = 1; i < n; i++) { // Iterate from second house
costs[i][0] += Math.min(costs[i - 1][1], costs[i - 1][2]); // Red: add min of previous Blue/Green
costs[i][1] += Math.min(costs[i - 1][0], costs[i - 1][2]); // Blue: add min of previous Red/Green
costs[i][2] += Math.min(costs[i - 1][0], costs[i - 1][1]); // Green: add min of previous Red/Blue
}
return Math.min(costs[n - 1][0], Math.min(costs[n - 1][1], costs[n - 1][2])); // Min of last house
}
public static void main(String[] args) {
System.out.println(new Solution().minCost(new int[][]{{17,2,17},{16,16,5},{14,3,19}}));
}
}
Java shown for learning; Interactive Lab below runs a JS twin (same tests / custom / main).
Walkthrough
[[17,2,17],[16,16,5],[14,3,19]] → 10. See lab tests.
Also asked: Job Prep DP · Amazon, Target
Pods & Scaling — LB → Pods → Cache → DB
Animated request path for a high-traffic retail service. Narrate HPA, cache miss, and DB as source of truth.
Ready
Client
→
LB
→
Pods
→
Cache
→
DB
Talk track: LB spreads connections; pods are stateless; cache absorbs hot reads; DB owns writes + invariants; scale pods via HPA before sharding.
L1. Car Rental System
Invariant: No confirmed reservations overlap for the same vehicle; money and reservation transitions are idempotent.
Problem / Scope
Pricing has to be locked into a quote long enough for a customer to decide, while the vehicle underneath that quote must not be promised to two renters at once across a window that spans hours or days, not a single instant.
Structure
Service
Domain
Repository
Client
RentalService is the Service layer orchestrating the hold→confirm→pickup→return lifecycle; Reservation/RentalAgreement/Quote are the Domain, carrying ReservationStatus transitions; VehicleRepository is the Repository, the only place availability search and locked single-vehicle reads happen; PricingStrategy and PaymentPort sit at the Client boundary as swappable collaborators.
APIs & Patterns
PricingStrategy.quote(type, period, addOns) is a Strategy: it lets pricing vary by vehicle type, rental length, and add-ons without RentalService knowing how any of that math works.
VehicleRepository.lockCandidate(id) returns an Optional<Vehicle> because the vehicle may already be gone by the time confirm runs, and the method name signals a pessimistic, lock-for-update read rather than a plain lookup; PaymentPort.authorize(..., idempotencyKey) pushes retry-safety for the charge down to the payment boundary itself.
Flow
A caller gets a Quote from quote(criteria), turns it into a short-lived hold(quote, customer), then calls confirm(holdId, payment, idempotencyKey) to authorize payment and lock in the reservation.
At the counter, pickup(reservationId, inspection) opens the RentalAgreement, and returnVehicle(agreementId, returnInspection) closes it out with a final Invoice.
Concurrency
The race to guard against is two confirms landing on the same vehicle for overlapping windows. lockCandidate takes a pessimistic read on the vehicle before confirm commits, so only one in-flight confirm can win; stale HELD reservations that never reach confirm transition to EXPIRED so abandoned holds don't permanently block inventory.
Scale bridge
Availability queries naturally shard by branch, so a multi-branch deployment partitions VehicleRepository by branchId rather than scanning one global table; holds move from a DB row lock to a short-TTL distributed lock per vehicle once concurrent hold volume outgrows single-row contention.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L2. Twitter / Social Feed
Invariant: A viewer sees only currently authorized posts, and retries never create duplicate posts or social edges.
Problem / Scope
A home feed has to feel current for both a user who follows a handful of people and one who follows thousands of high-volume accounts, and it can't recompute the full set of posts from every followed author on every page load.
Structure
Service
Domain
Repository
Client
FeedService is the Service orchestrating publish/home/follow/delete; Post/PostId/FeedCursor are the Domain, carrying authorship and ranking position; PostRepository and FollowGraph are the Repository layer for posts and social edges; FanoutStrategy and RankingStrategy sit at the Client boundary as pluggable delivery and ranking policy.
APIs & Patterns
FollowGraph.follows(userId, authorId)/followers(authorId, page) abstract the social graph away from storage, letting FeedService check authorization and enumerate an audience without caring how edges are stored; PostRepository.byAuthors(ids, cursor, limit) uses a FeedCursor(score, lastId) instead of an offset so pagination stays stable as new posts are inserted ahead of the page being read.
FanoutStrategy.distribute(post) is Strategy: it lets fan-out-on-write (push to follower feeds) and fan-out-on-read (pull at query time) be swapped per author without touching FeedService; RankingStrategy.rank(viewerId, candidates) separates ranking from retrieval so FeedService stays a thin orchestrator over both.
Flow
publish(authorId, draft, idempotencyKey) saves the post via PostRepository.save, then hands it to FanoutStrategy.distribute to push or mark it for pull.
home(viewerId, cursor, limit) gathers candidates (via byAuthors or the fan-out cache), passes them through RankingStrategy.rank, and returns a page keyed by the next FeedCursor; follow(userId, targetId) updates FollowGraph and delete(postId, actorId) checks authorship before removing a post.
Concurrency
Client retries on a flaky connection risk double-posting or double-following. publish is keyed by idempotencyKey so a repeated submit returns the existing post instead of creating a second one, and follow edges are upserted against a unique (userId, targetId) pair so a retried follow is a no-op rather than a duplicate row.
Scale bridge
Fan-out-on-write stops being affordable once an author's follower count gets large, so FanoutStrategy flips those accounts to fan-out-on-read merged at home() time; PostRepository shards by author, and feed read caches are invalidated and rebuilt asynchronously instead of updated synchronously on every publish.
record PostId(long value) {}
record FeedCursor(long score, PostId lastId) {}
interface FollowGraph { boolean follows(long userId, long authorId); Set<Long> followers(long authorId, Page page); }
interface PostRepository { Post save(Post post); List<Post> byAuthors(Set<Long> ids, FeedCursor cursor, int limit); }
interface FanoutStrategy { void distribute(Post post); }
interface RankingStrategy { List<Post> rank(long viewerId, List<Post> candidates); }
final class FeedService {
Post publish(long authorId, PostDraft draft, String idempotencyKey);
Page<Post> home(long viewerId, FeedCursor cursor, int limit);
void follow(long userId, long targetId); void delete(PostId postId, long actorId);
}
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L3. Parking Lot
Invariant: At most one active ticket/reservation owns a spot, and a spot cannot be both free and occupied.
Problem / Scope
Multiple entry gates claim spots from one shared pool at the same instant, so assignment has to be exclusive at the moment of claim, not just validated afterward, and advance reservations have to shrink the available pool before the reserving vehicle physically shows up.
Structure
Service
Domain
Repository
Client
ParkingService is the Service driving enter/pay/exit/reserve; Ticket/ParkingSpot/SpotState are the Domain tracking a spot's lifecycle; SpotRepository is the Repository and the only place a spot is claimed or released; SpotAllocationStrategy and FeeStrategy sit at the Client boundary as pluggable assignment and pricing policy.
APIs & Patterns
SpotAllocationStrategy.select(vehicle, gate, candidates) is a Strategy: it decides which already-available spot to offer (nearest-to-gate, size match, EV-aware) without touching ParkingService, and it only ever sees the candidate list SpotRepository has already filtered by type and state.
SpotRepository.claim(type, gateId) picks and marks a spot in one call rather than exposing a separate select-then-update, and release(spotId, long version) takes a version so a stale release can't silently undo a spot that's already been claimed again.
Flow
enter(vehicle, gateId, idempotencyKey) calls SpotRepository.claim to atomically grab a spot and opens a Ticket.
pay(ticketId, method) runs FeeStrategy.calculate(ticket, exitAt) to produce a Receipt, and exit(ticketId, gateId) checks payment, calls SpotRepository.release(spotId, version) to free the spot, and returns an ExitResult; reserve(type, period) pre-claims a spot for a future window.
Concurrency
The race is two gates claiming the same spot at once, which would park two vehicles in one space. claim(type, gateId) performs the select-and-mark as a single atomic repository operation rather than two separate calls, and release(spotId, version) uses optimistic versioning so a delayed or retried release can't overwrite a spot that's since been claimed by someone else.
Scale bridge
A single lot's spot table is fine as one shard; a multi-garage network partitions SpotRepository by garage so claim contention stays local to one facility, and live occupancy counts shown in-app are served from an eventually-consistent cache updated on each claim/release rather than read from the repository directly.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L4. Notification System
Invariant: One logical request may have retries, but every delivery attempt is auditable and respects current suppression policy.
Problem / Scope
Any one logical send can take several provider attempts across fallback channels before it succeeds or permanently fails, and that retrying must never turn into the recipient actually receiving the message twice, nor ignore a suppression preference set after the request was submitted.
Structure
Service
Domain
Repository
Client
NotificationService is the Service driving submit/process/callback; SendCommand/DeliveryPlan/NotificationStatus are the Domain tracking attempt state; PreferenceService and TemplateRenderer act as the Repository-like boundary supplying policy and content; ChannelSender implementations per Channel are the Client boundary to the outside world.
APIs & Patterns
ChannelSender is Strategy/Adapter: one implementation per Channel, each exposing channel() so a dispatcher can route a resolved DeliveryPlan to the right sender without a type switch in NotificationService.
PreferenceService.resolve(recipient, type, now) is called at process time rather than submit time, so a preference change (muting a type, quiet hours) made after submission is still honored; TemplateRenderer.render(key, locale, data) is kept separate from sending so one RenderedMessage shape works across every ChannelSender.
Flow
submit(command) persists the SendCommand under its requestId and returns a NotificationId immediately; process(id) then resolves the DeliveryPlan via PreferenceService, renders with TemplateRenderer, and calls the matching ChannelSender.send with an idempotency key derived from requestId.
handleCallback(callback) records the provider's async delivery or bounce result, and status(id) exposes the current NotificationStatus for polling clients.
Concurrency
A retried submit or a redelivered process must not re-send to the recipient. ChannelSender.send(message, idempotencyKey) pushes dedupe down to the provider itself using a key tied to requestId, so a repeated attempt resolves to the original send instead of a second message.
Scale bridge
submit and process split into an enqueue-then-worker pattern so a burst of requests doesn't block callers; PreferenceService lookups, needed on every notification, move behind a cache, and outbound sends get routed through a rate limiter ahead of ChannelSender.send to respect provider throughput limits.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L5. Payment Gateway
Invariant: Captured minus refunded money never exceeds authorized/captured limits, and every ledger transaction balances to zero.
Problem / Scope
Money moves through a strict sequence of states (authorize, capture, refund) via an external processor that can time out or fail mid-call, and a client retry on any of those calls must never be allowed to move more money than the original request intended.
Structure
Service
Domain
Repository
Client
PaymentService is the Service driving create/confirm/capture/refund; PaymentIntent/PaymentState/Capture/Refund are the Domain tracking the money lifecycle; Ledger is the Repository-equivalent system of record for balanced entries; ProcessorAdapter implementations, selected via RoutingStrategy, are the Client boundary to each external processor.
APIs & Patterns
ProcessorAdapter is the Adapter pattern, hiding each processor's quirks behind one authorize/capture/refund shape; RoutingStrategy.choose(intent, healthy) is Strategy, letting PaymentService pick a processor by health, cost, or currency without hardcoding any one provider.
Ledger.postBalanced(transactionId, entries) takes a list of entries rather than a single amount, enforcing double-entry bookkeeping at the API boundary so every capture or refund posts matched debit/credit lines that sum to zero; capture/refund/create all take an idempotencyKey because they're exactly the calls most likely to be retried by a client after a timeout.
Flow
create(cmd, idempotencyKey) opens a PaymentIntent in CREATED; confirm(intentId, token) calls the chosen ProcessorAdapter.authorize, moving it to REQUIRES_ACTION or AUTHORIZED.
capture(authorizationId, amount, idempotencyKey) calls the processor's capture and posts a balanced Ledger entry, moving the intent to CAPTURED; refund(paymentId, amount, idempotencyKey) calls refund and posts the offsetting entry.
Concurrency
The risk is a retried capture or refund hitting the processor or Ledger twice and moving money beyond what was intended. Every money-moving call carries an idempotencyKey so a repeated call with the same key returns the original result instead of re-executing, and postBalanced writes a transaction's entries atomically so a crash mid-post can never leave the ledger half-written and unbalanced.
Scale bridge
RoutingStrategy's notion of a healthy processor comes from a circuit-breaker/health feed refreshed out of band rather than checked synchronously per call; Ledger writes are append-only and partitioned by merchant/account, with final reconciliation against processor settlement reports happening asynchronously rather than trusted as final at capture time.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L6. Shopping Cart
Invariant: Each cart mutation is versioned and totals are derived from a consistent set of price, promotion, and fulfillment inputs.
Problem / Scope
A cart can hold lines from multiple sellers and fulfillment methods, and price, promotions, and stock can each change independently between when an item is added and when checkout runs, so totals have to be computed from one consistent snapshot of all three, not three separately-fetched answers.
Structure
Service
Domain
Repository
Client
CartService is the Service driving mutate/validateForCheckout; Cart with its CartItemKey-addressed lines and version() is the Domain; PricingPort/InventoryPort are the Repository-style read boundaries to external pricing and stock systems; PromotionRule implementations are the Client collaborators evaluated against the cart.
APIs & Patterns
CartItemKey(sku, sellerId, fulfillment) is a composite key so the same SKU from two sellers, or under two fulfillment methods, stays two distinct lines instead of being merged into one quantity; PricingPort.price(cart, context) and InventoryPort.check(quantities) isolate CartService from how pricing and stock are actually computed.
PromotionRule.evaluate(cartView, customer) is Strategy: each promotion is a self-contained rule returning an Adjustment, so a new promo type never touches Cart or CartService; Cart.merge(guest, policy) takes a MergePolicy because combining a logged-out cart into an account cart at login is a policy decision (max quantity, sum, or prefer existing), not a fixed rule.
Flow
setQuantity(key, quantity) and applyCoupon(code) mutate the Cart and bump its version(); CartService.mutate(cartId, command, expectedVersion) loads the cart, applies the command, and only persists if the stored version still matches expectedVersion.
validateForCheckout(cartId, context) re-runs PricingPort.price, InventoryPort.check, and every PromotionRule.evaluate together, right before payment, to produce one consistent CheckoutSnapshot.
Concurrency
The risk is a lost update: a price-refresh job and a user adding an item, mutating the same cart concurrently, each overwriting the other's change. mutate(cartId, command, expectedVersion) is optimistic concurrency control, rejecting the write if the cart has moved on since the caller last read it, forcing a re-read instead of silently clobbering state.
Scale bridge
PricingPort/InventoryPort calls at checkout time are the expensive synchronous dependency, so at scale they get short-TTL cached per SKU and only re-verified for lines that changed since the last check, instead of re-querying every line on every mutate.
record CartItemKey(String sku, String sellerId, String fulfillment) {}
interface PricingPort { PricedCart price(Cart cart, CustomerContext context); }
interface InventoryPort { Availability check(Map<String,Integer> quantities); }
interface PromotionRule { Adjustment evaluate(CartView cart, CustomerContext customer); }
final class Cart {
void setQuantity(CartItemKey key, int quantity); void applyCoupon(String code);
void merge(Cart guest, MergePolicy policy); long version();
}
final class CartService {
CartView mutate(UUID cartId, CartCommand command, long expectedVersion);
CheckoutSnapshot validateForCheckout(UUID cartId, CustomerContext context);
}
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L7. LRU / LFU Cache Service
Invariant: The key map and eviction metadata agree, and total admitted weight never remains above capacity after a mutation.
Problem / Scope
The cache must stay within a fixed capacity under either recency (LRU) or frequency (LFU) eviction semantics, and the bookkeeping needed to decide what to evict has to stay cheap enough not to become the bottleneck on what is supposed to be the fast path.
Structure
Service
Domain
Repository
Client
Cache<K,V> is the Service-level contract client code calls; the Entry<V> values plus whatever internal structure the eviction policy keeps (a recency list or frequency buckets) form the Domain; the entries map inside BoundedCache is the Repository, the actual key-value store; the plugged-in EvictionPolicy<K> (LRU, LFU, or a hybrid) is the Client collaborator deciding what gets evicted.
APIs & Patterns
EvictionPolicy<K> is Strategy: BoundedCache delegates every eviction decision to it via onRead/onWrite/onRemove/victim(), so swapping LRU for LFU means writing a new policy, not touching BoundedCache at all; victim() returns an Optional<K> so an empty or not-yet-full cache can say "nothing to evict" without a sentinel.
get returning Optional<V> and put taking a per-entry Duration ttl are deliberate: get has to express "absent or expired" without null, and put has to express per-entry expiry rather than one cache-wide timeout; stats() exposing hit/miss/eviction counts as a first-class method lets callers monitor effectiveness without external instrumentation.
Flow
put(key, value, ttl) stores or replaces the entry, calls policy.onWrite(key), then loops on policy.victim() to evict until the cache is back within capacity.
get(key) checks expiry, calls policy.onRead(key) to record the access, and returns Optional.empty() on a miss or expired entry; remove(key) deletes from the map and calls policy.onRemove(key) so the map and the policy's internal state never drift apart.
Concurrency
The invariant at risk is the entries map and the eviction policy's internal structures disagreeing under concurrent access — for instance an entry evicted by one thread's put while another thread's get is mid-read of it. Each of get/put/remove locks around the map mutation and its matching policy notification as one atomic pair, so the two structures update together or not at all.
Scale bridge
A single BoundedCache tops out at one process's memory and one lock's throughput, so past that it becomes sharded instances keyed by a hash of K (each shard its own lock-protected cache) or an external cache tier where eviction runs server-side and the local process holds only a thin L1 on top.
// Imports Java utility classes like Map, HashMap, Optional.
import java.util.*;
// Imports Java time classes like Duration for TTL handling.
import java.time.*;
/** Main class: exercises the BoundedCache skeleton with a placeholder FIFO policy. */
public class Main {
public static void main(String[] args) {
// The lesson's get/put/remove bodies are stubs (comments only), so the cache stores nothing yet:
// get returns Optional.empty and remove returns false regardless of what was put.
BoundedCache<String,String> cache = new BoundedCache<>(new FifoPolicy<>());
cache.put("a", "1", Duration.ofSeconds(5));
System.out.println("put done");
Optional<String> getResult = cache.get("a");
System.out.println("get: " + getResult); // Optional.empty (stub)
boolean removeResult = cache.remove("a");
System.out.println("remove: " + removeResult); // false (stub)
}
}
/** Cache and policy interfaces and BoundedCache implementation */
// Cache interface defining get, put, remove, and stats operations with generic key and value types.
interface Cache<K,V> { Optional<V> get(K key); void put(K key, V value, Duration ttl); boolean remove(K key); CacheStats stats(); }
// EvictionPolicy interface defining callbacks for read, write, remove, and victim selection for eviction.
interface EvictionPolicy<K> { void onRead(K key); void onWrite(K key); void onRemove(K key); Optional<K> victim(); }
// BoundedCache class implementing Cache with a map of entries and a pluggable eviction policy.
final class BoundedCache<K,V> implements Cache<K,V> {
// Internal map storing key to Entry<V> mappings for cached values.
private final Map<K, Entry<V>> entries = new HashMap<>();
// Reference to the eviction policy used to determine which entry to evict when capacity is exceeded.
private final EvictionPolicy<K> policy;
// Constructor accepting an EvictionPolicy instance to configure eviction behavior.
BoundedCache(EvictionPolicy<K> policy) { this.policy = policy; }
// Returns a new CacheStats instance (placeholder for statistics tracking).
public CacheStats stats() { return new CacheStats(); }
// Get method: locks, checks expiration, updates policy on read, returns value if present (stub returns empty).
public Optional<V> get(K key) { /* lock, expire/read, update policy */ return Optional.empty(); }
// Put method: replaces existing entry, evicts victims until within weight limits (stub).
public void put(K key, V value, Duration ttl) { /* replace, evict until within weight */ }
// Remove method: atomically removes entry from map and notifies policy (stub returns false).
public boolean remove(K key) { /* remove map + policy atomically */ return false; }
}
// Placeholder class for cache statistics (currently empty).
class CacheStats {}
// Placeholder class representing a cache entry holding a value (currently empty).
class Entry<V> {}
// FifoPolicy implements EvictionPolicy using first-in-first-out eviction order (stub methods).
class FifoPolicy<K> implements EvictionPolicy<K> {
// Callback when a key is read (no-op in FIFO).
public void onRead(K key) {}
// Callback when a key is written (no-op in FIFO).
public void onWrite(K key) {}
// Callback when a key is removed (no-op in FIFO).
public void onRemove(K key) {}
// Returns the next victim key for eviction (stub returns empty).
public Optional<K> victim() { return Optional.empty(); }
}
L8. Rate Limiter
Invariant: A decision atomically consumes all required quota or none; any approximation has an explicit, bounded overshoot.
Problem / Scope
A single request can draw against several overlapping limits at once — global, tenant, per-user, per-endpoint — and the limiter has to grant all of them together or none, rather than letting a request consume budget from one scope and then get rejected by the next.
Structure
Service
Domain
Repository
Client
RateLimiter.acquire is the Service-level decision API callers check before doing work; Descriptor (identifying the scope) and Decision (the outcome) are the Domain; PolicyRepository.resolve(descriptor) is the Repository, resolving which RateLimitPolicy applies; CompositeLimiter is the Client-facing composition of multiple limiter scopes into one call.
APIs & Patterns
Descriptor(tenant, api, principal) is a composite key, letting one limiter implementation serve per-tenant, per-API, or per-user limits just by varying which fields are populated; PolicyRepository.resolve(descriptor) separates what the limit is from whether this call is within it, so raising a tenant's quota doesn't require redeploying limiter logic.
TokenBucketLimiter implements RateLimiter is Strategy — a sliding-window or leaky-bucket implementation could sit behind the same acquire(key, cost, now) signature; CompositeLimiter.acquire(ctx, cost) chains global → tenant → principal → endpoint checks, modeling layered quotas as a pipeline instead of one limiter with four opaque counters.
Flow
A request arrives and CompositeLimiter.acquire(ctx, cost) resolves policy for each scope via PolicyRepository, then calls each underlying RateLimiter.acquire(key, cost, now) in order from global down to endpoint.
The first Decision with allowed=false short-circuits the chain and its retryAt is returned to the caller; only if every scope returns allowed=true does the request proceed.
Concurrency
The invariant is that acquire() must atomically consume cost or consume nothing — two concurrent callers both reading "1 token left" and both proceeding would overshoot the limit. TokenBucketLimiter.acquire performs refill-then-consume as one atomic operation (a compare-and-swap on bucket state, or an atomic check-and-decrement) so the read of remaining tokens and the decrement can't be split across two callers.
Scale bridge
A single in-process TokenBucketLimiter only works for one node; across many nodes the bucket state moves to a shared store with atomic increment, and at very high QPS strict atomicity gets traded for a local-approximation algorithm (periodically-synced sliding-window counters) with an explicit, bounded overshoot — exactly what the invariant chip allows for.
record Descriptor(String tenant, String api, String principal) {}
record Decision(boolean allowed, long remaining, Instant retryAt) {}
interface RateLimiter { Decision acquire(Descriptor key, int cost, Instant now); }
interface PolicyRepository { RateLimitPolicy resolve(Descriptor descriptor); }
final class TokenBucketLimiter implements RateLimiter {
Decision acquire(Descriptor key, int cost, Instant now) { /* atomic refill + consume */ return null; }
}
final class CompositeLimiter {
Decision acquire(RequestContext ctx, int cost); // global -> tenant -> principal -> endpoint
}
L9. Feature Toggle Service
Invariant: A published flag version is immutable, evaluation is deterministic for the same snapshot/context, and rollout assignment is stable.
Problem / Scope
Evaluation has to stay deterministic for a given subject and context even while admins are actively editing targeting rules, and a gradual percentage rollout must keep assigning the same subjects to the same in/out bucket across requests and across every service instance.
Structure
Service
Domain
Repository
Client
FlagEvaluator.evaluate is the hot-path Service read, with FlagAdminService as the write side; FlagSnapshot/FlagVersion/Evaluation<T> are the Domain, representing a point-in-time rule set and its result; FlagSnapshotProvider.current() is the Repository-equivalent supplying the active snapshot; Constraint and BucketingStrategy implementations are the Client-side pluggable targeting and rollout logic.
APIs & Patterns
Constraint.matches(context) is Strategy: each targeting rule (attribute match, allowlist, percentage window) implements the same interface so FlagEvaluator can combine arbitrary constraints without knowing their concrete type; Evaluation<T>(value, reason, version) returns the matched version and a human-readable reason alongside the value, making every decision explainable rather than a bare value.
BucketingStrategy.bucket(flagKey, subjectKey, salt) is a deterministic hash, not a random draw — hashing on flagKey+subjectKey+salt means the same subject always lands in the same bucket for a given flag's rollout, which is what keeps a percentage rollout stable instead of flickering per request; FlagSnapshotProvider.current() returning an immutable FlagSnapshot, rather than querying live storage per call, is what makes evaluate() deterministic within a request even if an admin publishes a change elsewhere mid-request.
Flow
An admin calls draft(command) to create a new FlagVersion, then publish(key, version) to make it the active snapshot.
At request time, evaluate(key, defaultValue, ctx) pulls the current snapshot, runs ctx through each Constraint.matches, and for subjects in a percentage rollout calls BucketingStrategy.bucket to decide in or out, returning an Evaluation with the matched version; rollback(key, version) republishes a prior version if the new one misbehaves.
Concurrency
The invariant is that a published FlagVersion is immutable and evaluate() never observes a half-published snapshot mixing old and new constraints. publish(key, version) swaps FlagSnapshotProvider's current snapshot via a single atomic reference swap (copy-on-write), so in-flight evaluate() calls see either the old snapshot in full or the new one in full, never a mix.
Scale bridge
FlagSnapshotProvider.current() is read on every request across every instance, so at scale it's served from an in-memory snapshot cached locally per instance and refreshed via pub/sub or polling rather than hit against a shared store synchronously on each evaluate() call; evaluation-reason logging moves from synchronous writes to an async event stream so it doesn't sit on the hot evaluation path.
record EvaluationContext(String subjectKey, Map<String,Object> attributes) {}
record Evaluation<T>(T value, String reason, long version) {}
interface Constraint { boolean matches(EvaluationContext context); }
interface BucketingStrategy { int bucket(String flagKey, String subjectKey, String salt); }
interface FlagSnapshotProvider { FlagSnapshot current(); }
final class FlagEvaluator {
<T> Evaluation<T> evaluate(String key, T defaultValue, EvaluationContext ctx);
}
final class FlagAdminService { FlagVersion draft(ChangeFlag command); void publish(String key, long version); void rollback(String key, long version); }
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L10. Restaurant Reservation
Invariant: Confirmed reservations and active holds never consume more compatible capacity than policy allows for a time range.
Problem / Scope
A reservation system has to match a party to a specific table (or combination of tables) on a live floor plan, under a hard rule that the same seating capacity can never be promised to two overlapping bookings — and it has to do this while a short-lived hold is still being decided on, not just at final confirmation.
Structure
Service
Domain
Repository
Client
Service is ReservationService orchestrating hold → confirm → seat; Domain is Party, Hold, Reservation, TablePlan and FloorSnapshot; Repository persists holds/reservations against the floor plan; Client is the diner-facing search-and-book flow calling into AvailabilityService.
APIs & Patterns
AvailabilityService.search returns SlotOffers for a restaurant/date given a Party, factoring in accessibilityNeeds before a table is even proposed. TableAssignmentStrategy.assign is a Strategy: different floor plans need different combining heuristics (single table vs. merging two four-tops for a party of six), so it returns Optional<TablePlan> because not every party fits the current FloorSnapshot.
DepositPort.authorize is a Port/Adapter to an external payment processor for the no-show deposit. confirm(holdId, guest, idempotencyKey) takes an idempotency key because a retried confirm click must not mint a second reservation off the same hold, and modify(id, change, version) takes a version so a concurrent edit doesn't silently overwrite a newer change.
Flow
A typical flow: search() returns candidate SlotOffers, the diner calls hold() on one to soft-lock capacity, confirm(holdId, guest, idempotencyKey) turns it into a durable Reservation (authorizing the deposit via DepositPort along the way), and on the day, checkIn() then seat() commit the assigned TablePlan.
Concurrency
The race is two diners holding overlapping time ranges on tables that can't coexist, which would double-book the floor. The Hold itself is the mechanism — a short-TTL pessimistic claim on capacity that must expire or resolve before another hold on the same tables/time range can succeed, and modify()'s version check prevents a stale edit from reinstating a plan that's already been superseded.
Scale bridge
At scale, holds move out of the primary table and into a fast expiring store (cache with TTL) so expiry doesn't depend on a sweep job, and availability search shards naturally by restaurantId since no query ever spans restaurants.
record Party(int size, List<String> accessibilityNeeds) {}
interface AvailabilityService { List<SlotOffer> search(UUID restaurantId, Party party, LocalDate date); }
interface TableAssignmentStrategy { Optional<TablePlan> assign(Party p, TimeRange r, FloorSnapshot floor); }
interface DepositPort { DepositResult authorize(UUID holdId, Money amount); }
final class ReservationService {
Hold hold(SlotOffer offer, Party party); Reservation confirm(UUID holdId, Guest guest, String idempotencyKey);
Reservation modify(UUID id, ChangeRequest change, long version); void checkIn(UUID id); Seating seat(UUID id);
}
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L11. WhatsApp Chat
Invariant: A sender client ID maps to one message, membership is validated at append, and sequence order is stable within a conversation.
Problem / Scope
Messages have to reach every device a user is signed into, survive a device being offline for hours, and never be duplicated or reordered even though the network between sender and server retries aggressively.
Structure
Service
Domain
Repository
Client
Service is ChatService handling send/sync/acknowledge; Domain is MessageEnvelope, ClientMessageId and Session; Repository is ConversationLog, the append-only per-conversation store; Client is each device app, which both sends with a locally-generated clientId and pulls catch-up via a cursor.
APIs & Patterns
ConversationLog.append is the single writer that assigns a monotonic sequence per conversationId — an append-only log pattern — and after(conversationId, sequence, limit) pages through it for devices reconnecting after being offline. SessionRegistry.sessions(userId) returns every live device session so a message can reach all of them, not just the one that happens to be connected right now.
FanoutService.deliver pushes a MessageEnvelope to each live Session, while ChatService.sync(userId, cursor) is the pull-based complement for devices that were offline and missed the push entirely — the same envelopes, two delivery paths.
Flow
Flow: send(command) calls ConversationLog.append, which assigns the next sequence and dedups on clientId; the resulting envelope goes through FanoutService.deliver to every session from SessionRegistry.sessions; each device calls acknowledge(userId, conversationId, sequence, type) for delivery/read receipts; a device that was offline later calls sync(cursor), which resolves to ConversationLog.after().
Concurrency
The race is a client retrying a send over a flaky connection and producing two messages, or two appends to the same conversation landing out of order. ClientMessageId is the idempotency key append() dedups on, so a retried send() returns the original envelope instead of creating a duplicate; sequence assignment is serialized per conversationId so ordering stays stable regardless of which replica handled the write.
Scale bridge
At scale, ConversationLog shards by conversationId so no single partition has to serialize the whole system's writes, and FanoutService.deliver to large group chats moves from a synchronous loop over recipients to an async per-recipient queue.
record ClientMessageId(String value) {}
record MessageEnvelope(UUID conversationId, long sequence, ClientMessageId clientId, byte[] ciphertext) {}
interface ConversationLog { MessageEnvelope append(SendMessage command); Page<MessageEnvelope> after(UUID conversationId, long sequence, int limit); }
interface SessionRegistry { Set<Session> sessions(long userId); }
interface FanoutService { void deliver(MessageEnvelope message, Set<Long> recipients); }
final class ChatService {
MessageEnvelope send(SendMessage command); SyncBatch sync(long userId, SyncCursor cursor);
void acknowledge(long userId, UUID conversationId, long sequence, ReceiptType type);
}
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L12. Live News Feed
Invariant: Every displayed entry references an eligible story version, and corrections/takedowns supersede stale indexed or cached versions.
Problem / Scope
Dozens of wire services can file near-identical stories about the same event within seconds of each other, and a later correction or legal takedown has to retroactively suppress every ranked, cached, or indexed copy of the story it supersedes — not just stop new ones from appearing.
Structure
Service
Domain
Repository
Client
Service is NewsFeedService serving feed()/publishVersion()/subscribe(); Domain is Story, StoryVersion, FeedContext and RankedStory; Repository is the story store keyed by StoryId and version; Client is the reader app paging the feed with a FeedCursor.
APIs & Patterns
StoryIngestor.ingest takes an idempotencyKey so a publisher's retried filing can't mint the story twice. Deduplicator.findCanonical checks an incoming StoryCandidate against existing clusters before a new StoryId is minted, which is what keeps five wire reports of the same event from becoming five feed entries.
RankingStrategy.rank and EligibilityRule.allows are both Strategy implementations: ranking algorithm and eligibility checks (geo-fencing, paywall, active takedowns) are evaluated per FeedContext rather than hardcoded into feed(), so either can change without touching the service itself.
Flow
Flow: a publisher's story goes through StoryIngestor.ingest(input, idempotencyKey); Deduplicator.findCanonical either merges it into an existing cluster or lets it become a new canonical story; a later correction calls publishVersion(id, version); readers hit feed(context, cursor, limit), which filters through EligibilityRule.allows then orders via RankingStrategy.rank; live readers also get pushed updates via subscribe(context).
Concurrency
The race is a correction or takedown (publishVersion) landing after a stale version has already been ranked into a cached feed page, so a retracted story keeps showing up. StoryVersion is monotonic per StoryId, and feed()/subscribe() must resolve against the latest version rather than one pinned at cache-fill time, invalidating any cached page that still points at a superseded version.
Scale bridge
At scale, rank() runs against a precomputed candidate set refreshed on an interval instead of recomputing per request, and subscribe() fanout moves from direct push to a pub-sub broker so a surge of breaking-news subscribers doesn't fan out synchronously from one process.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L13. Order Notification System
Invariant: One order milestone produces at most one logical communication per audience and policy version, despite duplicate events.
Problem / Scope
Order state changes arrive as events from an upstream order service that delivers at-least-once, which means the same milestone (e.g. "shipped") can show up more than once — but the customer should get exactly one email and one SMS for it, not one per duplicate delivery.
Structure
Service
Domain
Repository
Client
Service is OrderNotificationHandler reacting to events; Domain is OrderEvent, Transition and CommunicationPlan; Repository is TransitionStore, which records which transitions have already been applied; Client is the upstream order service emitting events and the downstream channel providers receiving sends.
APIs & Patterns
TransitionStore.apply(event) is the gate — keyed on orderId and orderVersion — that decides whether an OrderEvent represents a transition that's genuinely new before anything downstream fires; it's effectively a dedup ledger rather than a plain event log. CommunicationPolicy.plans is a Strategy mapping a Transition plus OrderSnapshot to the set of audiences/channels/templates (CommunicationPlan) that should fire.
NotificationPort.send is a Port/Adapter to the actual email/SMS/push provider, returning a NotificationId so retry(communicationId) can resend one specific failed communication later without re-deriving the whole plan from scratch.
Flow
Flow: the upstream service emits an OrderEvent; onOrderEvent(event) calls TransitionStore.apply(event), which accepts it only if orderVersion represents a new transition; accepted transitions go through CommunicationPolicy.plans(transition, order) to build one or more CommunicationPlans; each is dispatched via NotificationPort.send; failed sends get picked up later by retry(communicationId).
Concurrency
The race is the same OrderEvent delivered twice by the upstream event bus, which would otherwise trigger the same email twice. TransitionStore.apply() keyed on orderId+orderVersion makes a replayed event with the same version a no-op, so onOrderEvent() is idempotent at the transition layer instead of depending on the notification provider's own dedup (which can't see duplicate triggers, only duplicate sends).
Scale bridge
At scale, onOrderEvent moves from a direct call to a consumer on a durable queue so a burst of order updates doesn't block the order service itself, and high-volume channels like push batch multiple recipients per NotificationPort.send call instead of one call each.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L14. Warehouse Inventory Updater
Invariant: Every balance change is explained by an immutable movement, and reserved quantity cannot exceed policy-eligible stock.
Problem / Scope
The same SKU is physically split across many bins, lots, and warehouses, and reservations, picks, receipts, and transfers all have to agree on one number — available quantity — without ever letting reserved stock exceed what's actually sitting on a shelf.
Structure
Service
Domain
Repository
Client
Service is InventoryService exposing reserve/release/receive/pick/transfer/available; Domain is StockKey, Movement, Allocation and Reservation; Repository is InventoryLedger, storing every balance change as a Movement against a StockKey; Client is order-management systems and warehouse-floor scanners calling these operations.
APIs & Patterns
InventoryLedger.append is the only write path — every change (receive, pick, transfer) is recorded as an immutable Movement rather than mutating a stored balance in place, an event-sourced ledger; history(key) replays movements to reconstruct the current balance for a StockKey. append() takes an idempotencyKey because the same scan can be retried from an unreliable handheld device on the floor.
AllocationStrategy.allocate is a Strategy choosing which specific StockBalance (which bin, which lot) to draw from for a given sku+quantity — FIFO by lot, nearest-bin-first, whatever the warehouse's picking policy needs — pluggable without touching reserve() or pick() themselves.
Flow
Flow: receive(ReceiveStock) appends an inbound Movement; reserve(ReserveStock) calls AllocationStrategy.allocate against current balances to produce a Reservation; pick(PickStock) consumes that reservation and appends an outbound Movement; unused reservations are freed with release(reservationId), and cross-warehouse moves go through initiate(TransferStock).
Concurrency
The race is two reserve() calls against the same StockKey both succeeding against a balance that's already stale, overselling stock that's already spoken for. Because balance is derived from the ledger rather than stored directly, reserve() has to compute availability from the latest Movement history and append its own reservation movement atomically against it, so a concurrent reserve sees the updated total instead of a cached one.
Scale bridge
At scale, InventoryLedger shards by warehouseId since movements in one warehouse never need to coordinate with another, and available() moves from replaying history() on every call to reading a materialized balance view kept current by streaming the ledger.
record StockKey(String sku, UUID warehouseId, String bin, String lot) {}
interface InventoryLedger { Movement append(InventoryCommand command, String idempotencyKey); List<Movement> history(StockKey key); }
interface AllocationStrategy { Allocation allocate(String sku, int quantity, List<StockBalance> balances); }
final class InventoryService {
Reservation reserve(ReserveStock command); void release(UUID reservationId);
Movement receive(ReceiveStock command); PickResult pick(PickStock command);
Transfer initiate(TransferStock command); Availability available(String sku, UUID warehouseId);
}
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L15. Workflow Management System
Invariant: History is append-only and each persisted event drives a deterministic next decision; stale workers cannot advance an instance.
Problem / Scope
Long-running, multi-step business processes have external activities that can fail, time out, or need explicit compensation, and the engine has to resume exactly where a crashed worker left off — deterministically, from whatever history was already durably recorded, not from in-memory state that just vanished.
Structure
Service
Domain
Repository
Client
Service is WorkflowEngine driving start/signal/onActivityResult/cancel; Domain is WorkflowDefinition, Step, Signal and Activity; Repository is the durable, append-only event history kept per WorkflowId; Client is callers starting workflows and workers leasing tasks off the TaskQueue.
APIs & Patterns
WorkflowDefinition.next(state, signal) is deterministic — given the same WorkflowState and signal it always returns the same Step — which is exactly what lets the engine replay recorded history to rebuild state after a crash instead of trusting fragile in-memory progress. Activity.execute/compensate is the Saga pattern: compensate() is the explicit undo path invoked when a later step in the workflow fails.
TaskQueue.enqueue/lease(worker, ttl) is a leased work queue, not a delete-on-read one — lease() hands a task to one worker for a bounded ttl, so a worker that crashes mid-task doesn't lose the task, it just becomes re-claimable once the lease expires.
Flow
Flow: start(definition, version, input, idempotencyKey) creates a WorkflowId and calls WorkflowDefinition.next() to get the first Step, enqueuing an ActivityTask; a worker calls lease() to claim it, runs Activity.execute(), and the outcome comes back through onActivityResult(); the engine calls next() again with updated state to pick the following Step, with signal() and tickTimers() able to drive further transitions until cancel() ends it.
Concurrency
The race is two workers leasing and executing the same ActivityTask — either a bug double-leasing it, or a lease expiring while the original worker is still mid-execution — which would double-run a non-idempotent activity. lease(worker, ttl) is a time-bounded pessimistic lock on the task, so only its current holder's result is accepted by onActivityResult(); start()'s idempotencyKey plays the same role one level up, stopping duplicate start() calls from spawning two instances of the same logical workflow.
Scale bridge
At scale, both the event history and the TaskQueue shard by WorkflowId so one workflow's throughput never contends with another's, and tickTimers() moves from a single scheduler loop to a partitioned timer wheel so timer volume scales with the number of workers rather than one process.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L16. Connection Pool
Invariant: A physical connection is owned by at most one active lease and is returned only after reset or destruction.
Problem / Scope
Creating a connection (a DB socket, say) is expensive enough that it has to be shared across many concurrent callers, but sharing means guaranteeing the same physical connection is never handed to two callers at once, while still bounding how many exist and recovering cleanly from ones that go bad.
Structure
Service
Domain
Repository
Client
This is a single in-process data structure rather than a distributed service, so the usual tiers collapse: Service+Domain is ConnectionPool<C> itself; Repository is its internal idle/active connection bookkeeping (no external persistence at all); Client is application code calling acquire() to borrow a connection for one unit of work.
APIs & Patterns
ConnectionFactory<C> is a Factory/Strategy that isolates how a connection of type C is created, validated, reset, and destroyed from the pool's own borrow/return bookkeeping — the same pool logic works whether C is a JDBC connection or any other pooled resource. Lease<C> extends AutoCloseable so get() exposes the underlying connection and close() returns it via try-with-resources, with invalidate() as the escape hatch when the caller already knows the connection is broken.
acquire(Duration timeout) throwing TimeoutException is a bounded wait rather than an indefinite block, so a caller under backpressure fails fast instead of piling up behind an empty pool; resize(minIdle, maxSize) lets the pool's bounds change live without recreating it.
Flow
Flow: a caller calls acquire(timeout); the pool hands back an idle connection that passes ConnectionFactory.validate(), or create()s a new one if under maxSize and none are idle; the caller uses Lease.get(); on close() the connection is reset() and returned to idle, while invalidate() or a failed validate() routes it to destroy() instead; evictIdle(now) periodically destroys connections that have sat idle too long.
Concurrency
The race is two threads acquiring the same physical connection, or evictIdle() destroying a connection that's still out on lease. acquire() has to atomically move a connection from idle to leased before returning its Lease, so idle-set membership and "currently leased" are never true for the same connection at once; evictIdle() only ever considers connections still sitting in the idle set, never ones out on lease.
Scale bridge
At scale, one pool per process stops being the right lever — the real constraint is sizing maxSize against the downstream service's own connection ceiling and running a separate pool per downstream dependency, rather than making a single pool bigger.
interface ConnectionFactory<C> { C create(); boolean validate(C c); void reset(C c); void destroy(C c); }
interface Lease<C> extends AutoCloseable { C get(); void invalidate(); void close(); }
final class ConnectionPool<C> implements AutoCloseable {
Lease<C> acquire(Duration timeout) throws TimeoutException;
void resize(int minIdle, int maxSize); PoolStats stats();
void evictIdle(Instant now); public void close();
}
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L17. Custom HashMap
Invariant: Each equal key appears at most once, size matches reachable entries, and every entry resides in the bucket implied by its hash/capacity.
Problem / Scope
Building a key-value map from scratch means owning the hash-to-bucket mapping, collision handling, and growth policy yourself — the usual O(1)-average lookup only holds if the bucket array grows in step with the number of entries stored in it.
Structure
Service
Domain
Repository
Client
This is a single in-process data structure, not a distributed system, so the usual tiers map loosely onto it: table (the bucket array) stands in for Repository, and CustomHashMap's own public methods are both Service and Domain — there's no real network tiering here, worth noting as the contrast to the multi-layer systems elsewhere in this guide.
APIs & Patterns
table is an array of Node<K,V> buckets, each a singly-linked chain (separate chaining); index(hash) maps a key's hash into that array, and put/get/remove all walk the target bucket's chain comparing keys for equality. Node<K,V> implements Map.Entry<K,V> directly so iterator() can hand out entries without an extra wrapper allocation.
resize() fires once size exceeds threshold (capacity times load factor) — it allocates a larger table and rehashes every existing Node into its new bucket via index(), which is what keeps chain length, and so lookup time, roughly constant as the map grows. modCount exists purely to support fail-fast iteration, not for correctness of the map itself.
Flow
Flow: put(key, value) computes the key's hash, calls index(hash) to find the bucket, walks the chain to either replace an existing matching key's value or append a new Node, then checks size against threshold and triggers resize() if needed; get(key)/containsKey(key) do the same hash-and-walk without mutating anything.
Concurrency
As written this map isn't synchronized at all, so the real hazard is concurrent put/resize calls corrupting the bucket array, or a structural change happening mid-iteration. modCount is the fail-fast guard here — iterator() captures it at creation and each next() call checks it's unchanged, throwing ConcurrentModificationException rather than returning silently wrong results; actual thread-safety would need external synchronization or a lock-striped/CAS-based table, which this class deliberately doesn't provide.
Scale bridge
Past a single process's heap, this becomes a distributed hash table: index(hash) turns into consistent hashing across nodes, resize() turns into cluster rebalancing instead of a single array copy, and each shard runs its own local instance of exactly this structure underneath.
final class CustomHashMap<K,V> implements Iterable<Map.Entry<K,V>> {
private Node<K,V>[] table; private int size, threshold, modCount;
V put(K key, V value); V get(Object key); V remove(Object key);
boolean containsKey(Object key); int size(); void clear();
public Iterator<Map.Entry<K,V>> iterator();
private int index(int hash); private void resize();
private static final class Node<K,V> implements Map.Entry<K,V> {
final int hash; final K key; V value; Node<K,V> next;
}
}
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L18. Library Management System
Invariant: A physical copy has at most one active loan and hold allocation follows the configured queue and eligibility policy.
Problem / Scope
Many patrons want the same title at once, but each physical copy is a single unit that can only be on loan to one person at a time, and different item types (books vs. DVDs) carry different loan durations and renewal rules.
Structure
Service
Domain
Repository
Client
CirculationService is the Service, orchestrating checkout/renew/return/hold; Loan, Hold and BookCopy are the Domain records; CopyRepository is the Repository that locates and locks one physical copy by ISBN and branch.
APIs & Patterns
LoanPolicy and FinePolicy are Strategy interfaces: dueDate() computes a due date from patron and item type, canRenew() checks the HoldQueue so an item can't be renewed out from under a waiting patron, and FinePolicy.assess() keeps fine schedules independent of checkout logic.
CopyRepository.lockAvailable returns Optional<BookCopy> — the Optional signals "no copy available" without an exception, and "lock" implies a pessimistic read (row lock) so two kiosks can't both claim the last copy of a title.
Flow
checkout(patronId, copyId) calls lockAvailable() to claim a copy, then LoanPolicy.dueDate() to set the due date. renew(loanId) checks canRenew() against the HoldQueue before extending it. returnCopy() runs FinePolicy.assess() and frees the copy for the next Hold in queue.
Concurrency
The race is two patrons checking out the last available copy of a title at the same instant; lockAvailable()'s pessimistic row lock on the BookCopy row ensures only one checkout succeeds rather than relying on optimistic retry for a binary availability flag.
Scale bridge
A single branch's copy table works until the catalog spans a consortium of branches — hold allocation then needs to shard by ISBN or title id, with cross-branch availability eventually consistent and hold notification moved to an async event per return.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L19. Food Ordering System
Invariant: Order state moves only through allowed transitions, and payment/restaurant/dispatch side effects are linked idempotently to that order.
Problem / Scope
The real problem is coordinating three unreliable external parties — the payment gateway, the restaurant's acceptance, and courier dispatch — around a single order without double-charging or losing track of state if any step is retried.
Structure
Service
Domain
Repository
Client
OrderService is the Service driving the OrderState machine; Order/CheckoutCommand are the Domain; RestaurantPort, DispatchStrategy and PaymentPort are ports to the three external systems rather than part of the core domain.
APIs & Patterns
PaymentPort.authorize takes a String key (idempotency key) so a retried authorize call for the same checkout never double-authorizes the card; capture() and refund() are split out because authorization happens at order placement but capture only fires once the restaurant actually accepts.
RestaurantPort and DispatchStrategy are Strategy/Adapter interfaces: requestAcceptance() hides the restaurant's POS integration behind one call, and assign() returns Optional<CourierOffer> because failing to find a courier is an expected outcome, not an error.
Flow
place(command, idempotencyKey) authorizes payment and creates the order in PENDING_PAYMENT, moving to PLACED on success. RestaurantPort.requestAcceptance() drives a transition() to ACCEPTED, which triggers capture(), then PREPARING → READY. DispatchStrategy.assign() then picks a courier for PICKED_UP → DELIVERED.
Concurrency
A retried checkout request could create a duplicate order, and two transition() calls can race (say, the restaurant accepts while the customer cancels); the idempotencyKey on place() guards the first, and transition(id, action, expectedVersion) uses optimistic locking so a stale transition is rejected instead of silently overwriting newer state.
Scale bridge
At scale, the synchronous call into DispatchStrategy.assign() becomes an event (order-accepted, order-ready) on a queue, letting courier matching run as its own horizontally-scaled service instead of inline inside OrderService.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L20. Elevator System
Invariant: A car moves only with doors safely closed, opens only when level and stopped, and hardware safety interlocks always dominate scheduling.
Problem / Scope
Scheduling — which car should answer a given hall call — is a soft optimization problem, while door and motion safety are hard real-time constraints; the design has to guarantee the latter can never be overridden by the former, even if the scheduler is slow or wrong.
Structure
Service
Domain
Repository
Client
ElevatorController is the Service, the single entry point for hall/car requests and sensor events; ElevatorCar/CarState is the Domain holding each car's actual state machine; SystemSnapshot is the read model the scheduler consults in place of a live Repository.
APIs & Patterns
SchedulingStrategy.assign() is a Strategy pattern: it takes the pending HallRequest plus a SystemSnapshot, a consistent point-in-time view, so it never races with a car that's mid-move, and returns Optional<ElevatorId> because a request can legitimately go unassigned when every car is OUT_OF_SERVICE.
ElevatorHardware is an Adapter over the physical actuators — move/stop/openDoor/closeDoor are the only primitives a car may call — and onSensor(car, event) is how door-closed and floor-level feedback re-enters the controller, so transitions are driven by confirmed sensor state, not assumed motor commands.
Flow
hallRequest(floor, direction) asks SchedulingStrategy.assign() for a car given the current SystemSnapshot; the chosen car's addStop(floor) queues the stop; ElevatorCar.step(sensors) drives MOVING_UP/MOVING_DOWN toward it; onSensor confirms arrival, moving the car to DOORS_OPEN via openDoor(), then closeDoor() before it resumes.
Concurrency
The invariant to protect is a car moving with doors not fully closed, or opening doors between floors; this isn't guarded by a lock but by the state machine itself — move() is only reachable from a state that has already observed a door-closed sensor event, and openDoor() only from a state confirmed level and stopped.
Scale bridge
A single controller generalizes to a bank of elevators across zones by partitioning SystemSnapshot and SchedulingStrategy per zone, and sensor events move from direct calls to a message bus so a controller failover doesn't strand a car mid-transition.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L21. Splitwise / Expense Sharing
Invariant: For each expense, paid shares and owed shares each equal the total; all balance ledger transactions net to zero.
Problem / Scope
The hard part isn't recording who paid what, it's guaranteeing every split — equal, percentage, exact-amount — reconciles to the total to the cent, and that a group's net balance stays provably consistent as expenses get edited and settled over time, not just when first entered.
Structure
Service
Domain
Repository
Client
ExpenseService is the Service for add/edit/settle; Expense/OwedShare/Debt are the Domain records that must always sum correctly; BalanceLedger is the Repository, an append-only store of balance-affecting transactions rather than a mutable running total.
APIs & Patterns
SplitStrategy.split() is a Strategy pattern — equal/percentage/exact implementations all return List<OwedShare> that must sum to the input Money total, which is where any rounding remainder is deterministically assigned so paid-shares always equal owed-shares exactly.
BalanceLedger.append() takes a transactionId and a list of BalanceEntry that must themselves net to zero (every debit has a matching credit) — a ledger, not a mutable balance column, so balances(groupId) is derived by aggregation and can never drift from an update bug.
Flow
add(command, idempotencyKey) runs SplitStrategy.split() to get shares, then BalanceLedger.append() with balanced entries. settle(command, idempotencyKey) later appends another balanced entry closing part of the debt. Periodically SimplificationStrategy.simplify(sheet) collapses a cycle of pairwise debts into fewer net transfers for display.
Concurrency
Two members editing or settling the same expense concurrently, or a retried add() double-counting it, is the risk; edit(expenseId, command, expectedVersion) uses optimistic locking against lost updates, and idempotencyKey on add()/settle() ensures a retried call appends the same ledger entries at most once.
Scale bridge
At scale, BalanceLedger shards naturally by groupId, and balances() moves from summing every entry to a maintained read-model that's invalidated — not recomputed from scratch — on each append, since a group with years of history can't afford a full ledger scan per check.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L22. Vending Machine
Invariant: One transaction can consume at most one reserved item, and money is captured or refunded according to the observed dispense outcome.
Problem / Scope
Payment and physical dispensing are two separate failure domains — money can be captured while the motor jams, or a slot can empty between selection and payment — so the machine has to reconcile money state against physical sensor truth rather than trust either side alone.
Structure
Service
Domain
Repository
Client
VendingMachine is the Service, the state machine for one purchase session; SlotId/Denomination/PurchaseResult are the Domain; CashInventory is the Repository-like store of cash on hand that ChangeStrategy reads; Dispenser represents the physical motor hardware.
APIs & Patterns
PaymentStrategy is a Strategy over payment methods: authorize() reserves funds for the selected price without finalizing the charge, and refund() is a separate explicit call made only after the machine learns whether dispensing actually happened.
ChangeStrategy.makeChange() returns Optional<List<Denomination>> because the machine may genuinely be unable to make exact change from the current CashInventory, and that case has to short-circuit the purchase rather than dispense with the wrong change.
Flow
select(slot) then insert(cash) build toward the price; purchase(method) calls PaymentStrategy.authorize(), then Dispenser.dispense(slot); onSensor(event) reports whether the item actually dropped — if confirmed, ChangeStrategy.makeChange() runs and the authorization is captured; if not, refund() fires instead.
Concurrency
The invariant to protect is two purchase attempts both targeting the last unit in a slot; select()/purchase() effectively reserve that unit against the current stock count before authorize() is even called, so a concurrent purchase on an already-reserved last unit is rejected up front, and onSensor remains the sole source of truth for whether the reservation was fulfilled.
Scale bridge
A single machine scales to a fleet by moving CashInventory and restock(slot, quantity) records off-device into a central telemetry store, with each machine reporting dispense events asynchronously for remote stock monitoring instead of only tracking state locally.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L23. BookMyShow / Ticket Booking
Invariant: For a show, one seat has at most one live hold owner or confirmed booking, never both.
Problem / Scope
Many users browse and select the same handful of seats for a popular show at once, so the system needs a short-lived reservation step between "seat looks free" and "payment confirmed," with a strict TTL so an abandoned cart doesn't lock seats forever.
Structure
Service
Domain
Repository
Client
BookingService is the Service orchestrating hold → payment → confirm; SeatHold/Booking/SeatState are the Domain describing a seat's lifecycle per show; SeatInventory is the Repository, the source of truth for seat state per showId.
APIs & Patterns
SeatInventory.hold() takes a Duration ttl and an owner string — a temporary reservation, not a booking, so a seat held but never paid for returns to AVAILABLE automatically once the TTL expires, with no explicit cancel required.
PricingStrategy.quote() is a Strategy that takes a CustomerContext (membership, time-of-booking) so dynamic pricing is computed independently of seat-locking — pricing and inventory concerns never block each other.
Flow
hold(HoldSeats command) marks the chosen seats HELD and returns a SeatHold; PricingStrategy.quote() shows the price; confirm(holdId, payment, idempotencyKey) processes payment then calls SeatInventory.commit(holdId, paymentRef) to flip the seats to BOOKED; on failure or expiry, release(holdId) returns them to AVAILABLE.
Concurrency
The race is two users selecting the same seat in the gap between it showing as available and a hold being placed; hold() must atomically check-and-set seat state so only one of two concurrent calls on the same seats succeeds, and the loser is told immediately instead of discovering it at payment time.
Scale bridge
At scale, seat state for one show is the natural sharding key — all holds and bookings for a showId live on one partition — and a background sweeper reclaims expired holds instead of checking every seat's timer on every seat-map view.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L24. Cab Booking / Ride Hailing
Invariant: A driver and rider each have at most one active trip, and only the first valid offer acceptance creates the trip.
Problem / Scope
Matching a ride request against many nearby drivers whose positions are constantly changing is the easy half; the hard half is guaranteeing that even if the same ride offer reaches several drivers at once, exactly one acceptance turns into a trip and every other acceptance is a safe no-op.
Structure
Service
Domain
Repository
Client
RideService is the Service for request/accept/transition; RideRequest/Trip/TripAction are the Domain encoding the trip lifecycle; DriverAvailabilityStore is the Repository tracking which drivers are currently reservable.
APIs & Patterns
MatchingStrategy.rank() is a Strategy over a GeoSnapshot (a point-in-time view of nearby drivers), returning an ordered List<DriverCandidate> so the service can offer the ride to the next candidate if the first doesn't accept in time.
DriverAvailabilityStore.reserve() returns a boolean and takes a Duration ttl — a short-lived atomic claim, not a full booking, so a driver can only be mid-offer for one ride at a time, and an unanswered offer self-expires instead of permanently blocking that driver.
Flow
request(command, idempotencyKey) ranks candidates via MatchingStrategy from a GeoSnapshot, reserves the top candidate's availability, and sends the offer; accept(offerId, driver) creates the Trip if that reservation is still valid, then transition() drives it through states while updateLocation(driver, point, sequence) streams position; FareStrategy.finalFare(trip) computes the final charge on completion.
Concurrency
The race is two drivers — or a duplicated push notification to the same driver — both calling accept() on the same offerId; the atomic claim in reserve() means only the driver who won the reservation can have their accept() succeed, and transition(tripId, action, version)'s optimistic-locking version rejects a stale or duplicate call instead of creating a second trip.
Scale bridge
At scale, GeoSnapshot can't be a live global query — it becomes a geo-sharded index (geohash/H3 cells) refreshed from streamed updateLocation events, with matching run per cell so ranking a request only touches nearby drivers instead of scanning the whole city.
Design sketch. This outline shows structure only and is not meant to run as a single file, so it has no Run button. Use Copy to keep it as a starting point.
L25. Logging Framework
Invariant: Logging must not break the caller; accepted events are immutable and backpressure/drop behavior is explicit and measurable.
Problem / Scope
Logging sits on every hot path in the application, so it has to be near-zero-cost when disabled, never throw back into caller code, and have an explicit, bounded answer for what happens when log volume outpaces I/O — silently blocking the caller or buffering without limit are both unacceptable.
Structure
Service
Domain
Repository
Client
LoggerContext is the Service handing out Logger instances and owning config reload/shutdown; LogEvent is the Domain, an immutable record of one logging call; Appender is the Repository-equivalent sink that persists or ships events.
APIs & Patterns
Logger.isEnabled(level) lets a call site skip building expensive arguments entirely when a level is disabled, instead of paying for formatting on every call; log() takes a template plus Object... args specifically to defer formatting until a Formatter actually runs.
Filter.test() and Formatter.format() form a Chain-of-Responsibility/Strategy pipeline — Filter decides whether a LogEvent proceeds independent of level (say, by logger name or context), and Formatter is a swappable serialization strategy (JSON, plain text), so routing and formatting vary per Appender without duplicated logic.
Flow
Application code calls Logger.log(level, template, args); isEnabled(level) short-circuits if disabled; an immutable LogEvent is built; each Filter.test(event) is consulted, and on acceptance Formatter.format(event) produces bytes that Appender.append(event) writes, with flush() invoked per policy or at shutdown(timeout).
Concurrency
The failure to prevent is a slow Appender blocking application threads, or an unbounded internal queue causing an OOM under a log storm; Appender implementations apply a bounded queue with an explicit drop-or-block policy, and LoggerContext.shutdown(timeout) gives in-flight events a bounded window to flush rather than hanging the process.
Scale bridge
At scale, Appenders stop writing to local disk and instead batch-ship LogEvents asynchronously to a remote aggregation pipeline, with sampling or rate-limiting Filters added so one noisy logger can't starve the I/O budget shared across the fleet.