Skip to content
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 cheat sheet data structures algorithms quick reference interview prep

Continue Your Prep

Apply what you learned with our structured roadmaps and practice problems.