DSA 15 min read
DSA Cheat Sheet: Quick Reference for Data Structures & Algorithms
Essential DSA cheat sheet covering arrays, trees, graphs, DP patterns, sorting, and searching algorithms.
By SDE Roadmap
Arrays & Strings
Time Complexities
| Operation | Array | ArrayList | LinkedList |
|---|---|---|---|
| Access | O(1) | O(1) | O(n) |
| Search | O(n) | O(n) | O(n) |
| Insert (end) | O(1)* | O(1)* | O(1) |
| Insert (mid) | O(n) | O(n) | O(1) |
| Delete | O(n) | O(n) | O(1)* |
| Insert (beginning) | O(n) | O(n) | O(1) |
| Delete (beginning) | O(n) | O(n) | O(1) |
| Search (sorted) | O(log n) | O(log n) | O(n) |
| Reverse | O(n) | O(n) | O(n) |
| Sort | O(n log n) | O(n log n) | O(n log n) |
*Amortized O(1) for ArrayList/dynamic arrays due to occasional resizing.
Essential Patterns
Two Pointers (Sorted Array):
int left = 0, right = arr.length - 1;
while (left < right) {
int sum = arr[left] + arr[right];
if (sum == target) return new int[]{left, right};
else if (sum < target) left++;
else right--;
}
Sliding Window (Fixed Size):
int windowSum = 0, maxSum = 0;
for (int i = 0; i < n; i++) {
windowSum += arr[i];
if (i >= k) windowSum -= arr[i - k];
maxSum = Math.max(maxSum, windowSum);
}
Sliding Window (Variable Size):
int left = 0, windowSum = 0, maxLen = 0;
for (int right = 0; right < n; right++) {
windowSum += arr[right];
while (windowSum > target) {
windowSum -= arr[left];
left++;
}
maxLen = Math.max(maxLen, right - left + 1);
}
Fast & Slow Pointers (Cycle Detection):
int slow = 0, fast = 0;
do {
slow = arr[slow]; // move 1 step
fast = arr[arr[fast]]; // move 2 steps
} while (slow != fast);
// Cycle exists; find entry point
slow = 0;
while (slow != fast) {
slow = arr[slow];
fast = arr[fast];
}
return slow; // cycle entry
Prefix Sum:
int[] prefix = new int[n + 1];
for (int i = 0; i < n; i++) {
prefix[i + 1] = prefix[i] + arr[i];
}
// Range sum [l, r] = prefix[r+1] - prefix[l]
Hash Maps
Operations
| Operation | Average | Worst |
|---|---|---|
| Insert | O(1) | O(n) |
| Delete | O(1) | O(n) |
| Search | O(1) | O(n) |
| Size | O(1) | O(1) |
| Iteration | O(n) | O(n) |
| Merge (Union) | O(n) | O(n) |
Pattern: Two Sum
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[]{map.get(complement), i};
}
map.put(nums[i], i);
}
Pattern: Frequency Count
Map<Integer, Integer> freq = new HashMap<>();
for (int num : nums) {
freq.merge(num, 1, Integer::sum);
}
// Find elements appearing more than n/2 times
for (Map.Entry<Integer, Integer> e : freq.entrySet()) {
if (e.getValue() > nums.length / 2) return e.getKey();
}
Trees
Traversals
// Inorder: Left -> Root -> Right (sorted for BST)
void inorder(TreeNode node) {
if (node == null) return;
inorder(node.left);
System.out.println(node.val);
inorder(node.right);
}
// Preorder: Root -> Left -> Right (clone tree)
// Postorder: Left -> Right -> Root (delete tree)
// Level Order: BFS with Queue (level-by-level)
BST Operations
| Operation | Average | Worst |
|---|---|---|
| Search | O(log n) | O(n) |
| Insert | O(log n) | O(n) |
| Delete | O(log n) | O(n) |
| Min/Max | O(log n) | O(n) |
| Successor | O(log n) | O(n) |
| Predecessor | O(log n) | O(n) |
| Validate BST | O(n) | O(n) |
| LCA | O(log n) | O(n) |
Tree Height & Depth
// Max depth (recursive)
int maxDepth(TreeNode root) {
if (root == null) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
// Min depth (BFS stops at first leaf)
int minDepth(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
int depth = 1;
while (!q.isEmpty()) {
int size = q.size();
for (int i = 0; i < size; i++) {
TreeNode node = q.poll();
if (node.left == null && node.right == null) return depth;
if (node.left != null) q.offer(node.left);
if (node.right != null) q.offer(node.right);
}
depth++;
}
return depth;
}
Graphs
BFS Template
void bfs(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
// process node
if (node.left != null) queue.offer(node.left);
if (node.right != null) queue.offer(node.right);
}
}
}
DFS Template
void dfs(TreeNode node) {
if (node == null) return;
// process node
dfs(node.left);
dfs(node.right);
}
Graph BFS (Adjacency List)
void bfs(List<List<Integer>> adj, int start) {
boolean[] visited = new boolean[adj.size()];
Queue<Integer> queue = new LinkedList<>();
visited[start] = true;
queue.offer(start);
while (!queue.isEmpty()) {
int node = queue.poll();
for (int neighbor : adj.get(node)) {
if (!visited[neighbor]) {
visited[neighbor] = true;
queue.offer(neighbor);
}
}
}
}
Topological Sort (Kahn's Algorithm)
List<Integer> topologicalSort(List<List<Integer>> adj, int n) {
int[] inDegree = new int[n];
for (int i = 0; i < n; i++)
for (int j : adj.get(i)) inDegree[j]++;
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < n; i++)
if (inDegree[i] == 0) queue.offer(i);
List<Integer> result = new ArrayList<>();
while (!queue.isEmpty()) {
int node = queue.poll();
result.add(node);
for (int neighbor : adj.get(node)) {
inDegree[neighbor]--;
if (inDegree[neighbor] == 0) queue.offer(neighbor);
}
}
return result.size() == n ? result : new ArrayList<>(); // empty if cycle
}
Union-Find (Disjoint Set)
class UnionFind {
int[] parent, rank;
UnionFind(int n) {
parent = new int[n]; rank = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
}
int find(int x) {
if (parent[x] != x) parent[x] = find(parent[x]); // path compression
return parent[x];
}
void union(int x, int y) {
int px = find(x), py = find(y);
if (px == py) return;
if (rank[px] < rank[py]) parent[px] = py;
else if (rank[px] > rank[py]) parent[py] = px;
else { parent[py] = px; rank[px]++; }
}
}
Graph Complexity Summary
| Algorithm | Time | Space | Use Case |
|---|---|---|---|
| BFS | O(V + E) | O(V) | Shortest path (unweighted), level traversal |
| DFS | O(V + E) | O(V) | Cycle detection, connected components |
| Dijkstra | O((V + E) log V) | O(V) | Shortest path (weighted, non-negative) |
| Bellman-Ford | O(V * E) | O(V) | Shortest path (negative weights) |
| Floyd-Warshall | O(V^3) | O(V^2) | All-pairs shortest path |
| Topological Sort | O(V + E) | O(V) | Task scheduling, dependency resolution |
| Union-Find | O(α(n)) amortized | O(V) | Dynamic connectivity, cycle detection |
Sorting Algorithms
| Algorithm | Time (Best) | Time (Avg) | Time (Worst) | Space | Stable |
|---|---|---|---|---|---|
| Bubble | O(n) | O(n^2) | O(n^2) | O(1) | Yes |
| Selection | O(n^2) | O(n^2) | O(n^2) | O(1) | No |
| Insertion | O(n) | O(n^2) | O(n^2) | O(1) | Yes |
| Merge | O(n log n) | O(n log n) | O(n log n) | O(n) | Yes |
| Quick | O(n log n) | O(n log n) | O(n^2) | O(log n) | No |
| Heap | O(n log n) | O(n log n) | O(n log n) | O(1) | No |
| Counting | O(n + k) | O(n + k) | O(n + k) | O(k) | Yes |
| Radix | O(d * n) | O(d * n) | O(d * n) | O(n + k) | Yes |
| Bucket | O(n + k) | O(n + k) | O(n^2) | O(n) | Yes |
When to Use Which
- Quick Sort: Default choice, fastest in practice, cache-friendly.
- Merge Sort: When stable sort is needed or data is linked/external.
- Heap Sort: When O(1) space and O(n log n) guarantee is needed.
- Counting Sort: When range of values k is small relative to n.
- Radix Sort: When sorting integers with known digit count.
- Insertion Sort: When n is very small (< 20) or data is nearly sorted.
- Tim Sort (Java's Arrays.sort for objects): Hybrid of merge and insertion, stable.
Dynamic Programming
Memoization Template (Top-Down)
Map<Integer, Integer> memo = new HashMap<>();
int solve(int n) {
if (n == 0) return baseCase;
if (memo.containsKey(n)) return memo.get(n);
int result = // recurrence using solve(n-1), solve(n-2), etc.
memo.put(n, result);
return result;
}
Tabulation Template (Bottom-Up)
int[] dp = new int[n + 1];
dp[0] = baseCase;
for (int i = 1; i <= n; i++) {
dp[i] = // recurrence using dp[i-1], dp[i-2], etc.
}
return dp[n];
2D DP Template
int[][] dp = new int[m + 1][n + 1];
dp[0][0] = baseCase;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
dp[i][j] = // recurrence using dp[i-1][j], dp[i][j-1], dp[i-1][j-1]
}
}
return dp[m][n];
Classic Problems
| Problem | Recurrence | Pattern |
|---|---|---|
| Fibonacci | dp[i] = dp[i-1] + dp[i-2] | 1D linear |
| Climbing Stairs | dp[i] = dp[i-1] + dp[i-2] | 1D linear |
| Coin Change | dp[i] = min(dp[i-coin] + 1) | 1D unbounded |
| 0/1 Knapsack | dp[i][w] = max(include, exclude) | 2D |
| Longest Common Subseq | dp[i][j] = max(dp[i-1][j], dp[i][j-1]) | 2D |
| Edit Distance | dp[i][j] = min(insert, delete, replace) | 2D |
| Longest Increasing Subseq | dp[i] = max(dp[j] + 1) for j < i | 1D O(n^2) |
| Maximum Subarray Sum | dp[i] = max(arr[i], dp[i-1] + arr[i]) | Kadane's |
| House Robber | dp[i] = max(dp[i-2] + arr[i], dp[i-1]) | 1D linear |
| Word Break | dp[i] = dp[j] && dict.contains(s[j:i]) | 1D |
| Burst Balloons | dp[i][j] = max(dp[i][k] + dp[k+1][j] + ...) | Interval DP |
| Longest Palindromic Subseq | dp[i][j] = dp[i+1][j-1] + 2 if match | Interval DP |
| Unique Paths | dp[i][j] = dp[i-1][j] + dp[i][j-1] | 2D grid |
| Minimum Path Sum | dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + grid[i][j] | 2D grid |
DP Pattern Recognition Guide
- Linear DP: Problem depends on previous 1-2 states (Fibonacci, climbing stairs, house robber).
- 2D Grid DP: Navigate grid, choose paths (unique paths, min path sum).
- Knapsack variants: 0/1 (each item once), unbounded (repeat), multiple (limited copies).
- Interval DP: Optimal way to merge/parenthesize (matrix chain, burst balloons).
- String DP: Subsequence/substring problems (LCS, edit distance, palindrome).
- Bitmask DP: Small n (<= 20), subset enumeration (TSP, assignment).
Binary Search
Standard Template
int binarySearch(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) return mid;
else if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
Leftmost (First Occurrence)
int leftBound(int[] arr, int target) {
int left = 0, right = arr.length - 1, result = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] >= target) {
if (arr[mid] == target) result = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return result;
}
Rightmost (Last Occurrence)
int rightBound(int[] arr, int target) {
int left = 0, right = arr.length - 1, result = -1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] <= target) {
if (arr[mid] == target) result = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}
Search in Rotated Sorted Array
int searchRotated(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) return mid;
if (arr[left] <= arr[mid]) { // left half sorted
if (target >= arr[left] && target < arr[mid]) right = mid - 1;
else left = mid + 1;
} else { // right half sorted
if (target > arr[mid] && target <= arr[right]) left = mid + 1;
else right = mid - 1;
}
}
return -1;
}
Binary Search on Answer
// Find minimum value that satisfies condition
int lo = minVal, hi = maxVal;
while (lo < hi) {
int mid = lo + (hi - lo) / 2;
if (isSatisfied(mid)) hi = mid;
else lo = mid + 1;
}
return lo;
Binary Search Complexity Summary
| Variant | Time | Space | Notes |
|---|---|---|---|
| Standard | O(log n) | O(1) | Exact match |
| Left/Right bound | O(log n) | O(1) | First/last occurrence |
| Rotated array | O(log n) | O(1) | Pivot detection |
| 2D matrix | O(log(m*n)) | O(1) | Row-col sorted |
| Answer binary search | O(log(range) * check) | O(1) | Monotonic predicate |
Quick Reference
Data Structure Comparison
| Data Structure | Access | Search | Insert | Delete | Use Case |
|---|---|---|---|---|---|
| Array | O(1) | O(n) | O(n) | O(n) | Fast access, fixed size |
| ArrayList | O(1) | O(n) | O(1)* | O(n) | Dynamic array, fast access |
| LinkedList | O(n) | O(n) | O(1) | O(1) | Frequent insert/delete |
| Stack | O(n) | O(n) | O(1) | O(1) | LIFO, undo, DFS |
| Queue | O(n) | O(n) | O(1) | O(1) | FIFO, BFS |
| Deque | O(1) | O(n) | O(1) | O(1) | Both ends, sliding window |
| HashMap | N/A | O(1) | O(1) | O(1) | Key-value lookups |
| TreeMap | N/A | O(log n) | O(log n) | O(log n) | Sorted keys, range queries |
| HashSet | N/A | O(1) | O(1) | O(1) | Unique elements, membership |
| Heap | N/A | O(n) | O(log n) | O(log n) | Priority queue, min/max |
| BST | O(log n) | O(log n) | O(log n) | O(log n) | Sorted data, range queries |
| Trie | O(L) | O(L) | O(L) | O(L) | Prefix search, autocomplete |
| Graph (adj list) | - | O(V+E) | O(1) | O(E) | Sparse graphs, traversal |
| Graph (adj matrix) | - | O(1) | O(1) | O(1) | Dense graphs, quick lookup |
Stack & Queue Templates
// Monotonic Stack (Next Greater Element)
int[] nextGreater(int[] arr) {
int[] result = new int[arr.length];
Stack<Integer> stack = new Stack<>();
for (int i = arr.length - 1; i >= 0; i--) {
while (!stack.isEmpty() && stack.peek() <= arr[i]) stack.pop();
result[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(arr[i]);
}
return result;
}
// LRU Cache using LinkedHashMap
LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<>(capacity, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > capacity;
}
};
Heap / Priority Queue
// Min-heap
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
// Max-heap
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
// Custom comparator
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] - b[1]);
String Operations
| Operation | Time | Notes |
|---|---|---|
| Concatenation | O(n) | Use StringBuilder |
| Substring | O(n) | Java creates new string |
| Search (naive) | O(n*m) | Brute force |
| Search (KMP) | O(n+m) | Pattern matching |
| Reverse | O(n) | Two pointers |
| Palindrome check | O(n) | Two pointers from center |
| Anagram check | O(n) | Frequency count or sort |
Common Interview Shortcuts
| Pattern | When to Use | Time |
|---|---|---|
| Two Sum (HashMap) | Find pair with sum | O(n) |
| Sliding Window | Subarray/substring problems | O(n) |
| Fast & Slow Pointers | Cycle detection, middle of list | O(n) |
| Merge Intervals | Overlapping intervals | O(n log n) |
| Top K Elements | K largest/smallest | O(n log k) |
| Binary Search | Sorted/search space | O(log n) |
| BFS (Queue) | Shortest path, level order | O(V+E) |
| DFS (Stack/Recursion) | Path finding, connected components | O(V+E) |
| Union-Find | Dynamic connectivity | O(α(n)) |
| Trie | Prefix matching, autocomplete | O(L) |
| Monotonic Stack | Next greater/smaller element | O(n) |
| Kadane's Algorithm | Maximum subarray sum | O(n) |
| Topological Sort | Task ordering with dependencies | O(V+E) |
| Bit Manipulation | Subset enumeration, XOR tricks | O(1) per op |
Resources
- DSA Roadmap - Complete learning path
- Practice Problems - Practice problems
- Topic Deep Dives - Detailed explanations
- Coding Patterns - 15 essential patterns
- DSA Guide - Full reference
Continue Your Prep
Apply what you learned with our structured roadmaps and practice problems.