Coding round in Java¶
How to run the round¶
- Restate the problem and confirm input/output with an example.
- Ask: size of input, duplicates, negatives, empty input, sorted?
- Say the brute force and its complexity.
- Improve: which pattern fits? Say the new complexity before coding.
- Code cleanly, naming things well.
- Test out loud with the example and one edge case; fix bugs calmly.
Patterns to recognise¶
| Signal in the problem | Pattern | Example |
|---|---|---|
| Find pairs / complements, counts | HashMap | Two sum, anagrams, first unique |
| Sorted array, pairs, in-place | Two pointers | Pair with sum, remove duplicates, container with most water |
| Contiguous subarray/substring with a condition | Sliding window | Longest substring without repeats, max sum of size k |
| Top / smallest k, streaming | Heap (PriorityQueue) |
Top-k frequent, k closest, merge k lists |
| Sorted input, "minimum that satisfies" | Binary search | Search rotated array, first bad version |
| Shortest path in unweighted grid/graph | BFS | Number of islands, word ladder |
| All combinations / paths | DFS / backtracking | Subsets, permutations |
| Matching brackets, next greater | Stack | Valid parentheses, daily temperatures |
| Overlapping intervals | Sort by start | Merge intervals, meeting rooms |
| Optimal over choices with overlap | Dynamic programming | Climbing stairs, coin change, LCS |
Must-know solutions¶
Two sum (HashMap, O(n))
Longest substring without repeating characters (sliding window, O(n))
int lengthOfLongestSubstring(String s) {
Map<Character, Integer> last = new HashMap<>();
int best = 0;
for (int left = 0, right = 0; right < s.length(); right++) {
char c = s.charAt(right);
if (last.containsKey(c) && last.get(c) >= left) left = last.get(c) + 1;
last.put(c, right);
best = Math.max(best, right - left + 1);
}
return best;
}
LRU cache (asked very often)
// Short version: LinkedHashMap in access order
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
LRUCache(int capacity) { super(16, 0.75f, true); this.capacity = capacity; }
@Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > capacity; }
}
HashMap<K, Node> plus a doubly linked list; get moves the node to the head, put
inserts at the head and evicts the tail when full; both O(1). Thread-safe version: wrap with a lock, or use
Caffeine in real code.
Top-k frequent elements (heap, O(n log k))
List<Integer> topK(int[] nums, int k) {
Map<Integer, Integer> count = new HashMap<>();
for (int n : nums) count.merge(n, 1, Integer::sum);
PriorityQueue<Map.Entry<Integer, Integer>> heap =
new PriorityQueue<>(Map.Entry.comparingByValue()); // min-heap by count
for (var e : count.entrySet()) {
heap.offer(e);
if (heap.size() > k) heap.poll();
}
List<Integer> out = new ArrayList<>();
while (!heap.isEmpty()) out.add(heap.poll().getKey());
Collections.reverse(out);
return out;
}
Valid parentheses (stack)
boolean isValid(String s) {
Deque<Character> stack = new ArrayDeque<>();
for (char c : s.toCharArray()) {
switch (c) {
case '(' -> stack.push(')');
case '[' -> stack.push(']');
case '{' -> stack.push('}');
default -> { if (stack.isEmpty() || stack.pop() != c) return false; }
}
}
return stack.isEmpty();
}
Merge intervals (sort, O(n log n))
int[][] merge(int[][] intervals) {
Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]));
List<int[]> out = new ArrayList<>();
for (int[] cur : intervals) {
if (out.isEmpty() || out.get(out.size() - 1)[1] < cur[0]) out.add(cur);
else out.get(out.size() - 1)[1] = Math.max(out.get(out.size() - 1)[1], cur[1]);
}
return out.toArray(new int[0][]);
}
Number of islands (BFS/DFS on a grid)
int numIslands(char[][] g) {
int count = 0;
for (int r = 0; r < g.length; r++)
for (int c = 0; c < g[0].length; c++)
if (g[r][c] == '1') { count++; sink(g, r, c); }
return count;
}
void sink(char[][] g, int r, int c) {
if (r < 0 || c < 0 || r >= g.length || c >= g[0].length || g[r][c] != '1') return;
g[r][c] = '0';
sink(g, r + 1, c); sink(g, r - 1, c); sink(g, r, c + 1); sink(g, r, c - 1);
}
Reverse a linked list
Binary search (first index ≥ target)
Max profit from one buy and one sell (finance favourite)
Mini order book (a domain twist they might give you)
// Price-time priority with TreeMaps of FIFO queues. Prices as long (paise), never double.
record Order(long id, boolean buy, long price, long qty) {}
final class OrderBook {
private final TreeMap<Long, ArrayDeque<long[]>> bids = new TreeMap<>(Comparator.reverseOrder());
private final TreeMap<Long, ArrayDeque<long[]>> asks = new TreeMap<>();
/** Returns trades as {buyId, sellId, price, qty}. long[] = {id, remainingQty}. */
List<long[]> submit(Order o) {
List<long[]> trades = new ArrayList<>();
TreeMap<Long, ArrayDeque<long[]>> opposite = o.buy() ? asks : bids;
long remaining = o.qty();
while (remaining > 0 && !opposite.isEmpty()) {
var best = opposite.firstEntry();
boolean crosses = o.buy() ? best.getKey() <= o.price() : best.getKey() >= o.price();
if (!crosses) break;
ArrayDeque<long[]> queue = best.getValue();
long[] resting = queue.peekFirst();
long fill = Math.min(remaining, resting[1]);
trades.add(o.buy() ? new long[] {o.id(), resting[0], best.getKey(), fill}
: new long[] {resting[0], o.id(), best.getKey(), fill});
remaining -= fill;
resting[1] -= fill;
if (resting[1] == 0) queue.pollFirst();
if (queue.isEmpty()) opposite.pollFirstEntry();
}
if (remaining > 0) {
(o.buy() ? bids : asks).computeIfAbsent(o.price(), p -> new ArrayDeque<>())
.addLast(new long[] {o.id(), remaining});
}
return trades;
}
}
ArrayDeque would make cancel O(n)."
Complexity cheat sheet¶
| Structure | Access | Search | Insert | Delete |
|---|---|---|---|---|
Array / ArrayList |
O(1) | O(n) | O(1) amortised at end, O(n) middle | O(n) |
HashMap / HashSet |
– | O(1) avg | O(1) avg | O(1) avg |
TreeMap / TreeSet |
– | O(log n) | O(log n) | O(log n) |
PriorityQueue |
O(1) peek | O(n) | O(log n) | O(log n) poll |
ArrayDeque |
O(1) ends | O(n) | O(1) ends | O(1) ends |
| Sorting | O(n log n) |