BigN Interview Primer

A curated primer for Data Structures & Algorithms that helps you tackle coding interviews at top tech companies.

Skip the overwhelming textbooks. This primer focuses on the patterns that actually show up in FAANG interviews — the ones that unlock 80% of coding questions.

Learn to recognize problems, apply proven techniques, and tackle LeetCode questions with confidence. Everything you need to ace your next technical interview.

💡 Patterns > Memorization

Problem-Solving Approach

Before diving into code, develop a systematic approach. This mental framework will save you from getting stuck mid-interview.

🧠 Mental Process

  1. Solve it in your head first - Don’t touch the keyboard yet
  2. Note the runtime - What’s the Big O complexity?
  3. Match patterns - Which data structures and algorithms fit?
  4. Code translation - Now the mechanical part (just muscle memory!)

📚 Knowledge Baseline

Master these fundamentals and you can tackle most interview problems:

Data Structures

Linked ListsTrees & TriesStacks & QueuesHeapsArraysHash Maps

Algorithms

DFSBFSBinary SearchMerge SortQuick Sort

Concepts

Bit ManipulationRecursionDynamic ProgrammingBig O

🎯 Interview Success Formula

Pattern Recognition + Muscle Memory = Interview Success

Once you recognize the pattern, implementation becomes mechanical. Practice until data structure operations are second nature.

Arrays & Lists

Arrays are the bread and butter of coding interviews. Master these patterns and you’ll handle most array problems with confidence.

🎯 Core Tips & Tricks

Several non-nested loops is still O(n)
Don’t overthink complexity
Sometimes starting from the back helps
Reverse iteration can simplify logic
Sorting could make the problem simpler
Trade O(n log n) time for cleaner algorithm
Binary search if structured well
Look for ordering constraints

↔️ Two Pointer Patterns

Slow & Fast

Detect cycles, find middle element

Inside Out

Expand from center (palindromes)

Outside In

Shrink from ends (two sum, containers)

⚡ Fast O(1) Deletion Trick

When you don’t care about order, avoid expensive O(n) array shifting:

  1. Find index of element to delete
  2. Swap it with the last element
  3. Decrement capacity and track current “size”

Strings

String problems are everywhere in coding interviews. Two fundamental patterns you’ll see repeatedly: palindromes and anagrams. Master these and you’re golden.

🔄 Palindrome Check

“racecar” - Use 2 pointers from start and end, compare towards the middle. Or start from middle and expand outwards (check odd/even length first).

boolean isPalindrome(String s) {
    // Two pointers: start and end, compare towards middle
    for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
        if (s.charAt(i) != s.charAt(j)) {
            return false;
        }
    }
    return true;
}

🔀 Anagram Detection

“cat”, “tac” - Same letters, different order. Two approaches with different tradeoffs:

Approach 1: Sorting (Simple)

boolean isAnagram(String s, String t) {
    // Simple approach: sort both strings and compare
    char[] sChars = s.toCharArray();
    char[] tChars = t.toCharArray();
    Arrays.sort(sChars);
    Arrays.sort(tChars);
    return String.valueOf(sChars).equals(String.valueOf(tChars));
    // Time: O(s log s) + O(t log t), Space: O(max(s, t))
}

Approach 2: Hash Map (Optimal)

boolean isAnagram(String s, String t) {
    // Optimized: character frequency counting
    if (s.length() != t.length()) return false;

    Map<Character, Integer> map = new HashMap<>();

    // Count characters in s
    for (char c : s.toCharArray()) {
        map.put(c, map.getOrDefault(c, 0) + 1);
    }

    // Decrement for characters in t
    for (char c : t.toCharArray()) {
        if (!map.containsKey(c)) return false;
        map.put(c, map.get(c) - 1);
        if (map.get(c) == 0) map.remove(c);
    }

    return map.isEmpty();
    // Time: O(max(s, t)) ~ O(n), Space: O(1) for lowercase ASCII
}

💡 String Patterns to Remember

  • Two pointers for palindromes and string comparisons
  • Hash maps for character frequency counting
  • Sliding window for substring problems
  • StringBuilder for efficient concatenation (Java)

Unicode Note: For ASCII-only strings, array counting works great. For Unicode support, stick with HashMap - UTF-8/16/32 encodings support 1MM-4MM characters!

Hash Maps & Sets

Hash Maps are your best friend for O(1) lookups. Master these patterns and you’ll solve 80% of “hard” problems in O(n) time.

🎯 Core Use Cases

Fast Lookups

Two Sum, Contains Duplicate, Valid Anagram

Frequency Counting

Character counting, finding duplicates

Caching Results

Memoization in DP, seen states in DFS

Grouping Data

Group anagrams, partition problems

🔍 Classic Pattern: Two Sum

The quintessential HashMap problem - store what you need, lookup what you have.

public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();

    for (int i = 0; i < nums.length; i++) {
        if (map.containsKey(nums[i])) {
            return new int[]{map.get(nums[i]), i};
        }
        map.put(target - nums[i], i);
    }
    return new int[]{-1, -1}; // not found
}

📊 Frequency Counting Pattern

Essential for anagrams, character analysis, and finding duplicates.

// Character frequency counting pattern
Map<Character, Integer> freq = new HashMap<>();
for (char c : s.toCharArray()) {
    freq.put(c, freq.getOrDefault(c, 0) + 1);
}

🔄 Map Iteration

// Iterating over a map - the right way
for (Map.Entry<K, V> entry : map.entrySet()) {
    System.out.println(entry.getKey());
    System.out.println(entry.getValue());
}

Java Map Tips

Use TreeMap instance, not SortedMap interface - Access to firstEntry(), lastEntry() extra methods
LinkedHashMap preserves insertion order - Useful for LRU cache implementations
TreeMap maintains sorted order - O(log n) operations but sorted iteration
getOrDefault() is your friend for frequency counting

Memory tip: HashMap = O(1) access, TreeMap = O(log n) order, LinkedHashMap = O(1) with insertion history

Stacks & Queues

Linear data structures with restricted access patterns. Essential for parsing, BFS/DFS, and many algorithmic patterns.

📚 Stack (LIFO)

Last In, First Out - like a stack of plates

// Use Deque instead of Stack (better practice)
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1);    // add to top
stack.push(2);
stack.peek();     // returns 2 (top element)
stack.pop();      // removes and returns 2

🚶 Queue (FIFO)

First In, First Out - like a line of people

// Use Deque instead of Queue interface
Deque<Integer> queue = new ArrayDeque<>();
queue.offer(1);   // add to back
queue.offer(2);
queue.peek();     // returns 1 (front element)
queue.poll();     // removes and returns 1

🔍 Classic Stack Problem

Valid Parentheses - the quintessential stack problem every engineer should know.

// Classic stack problem: Valid Parentheses
public boolean isValid(String s) {
    Deque<Character> stack = new ArrayDeque<>();

    for (char c : s.toCharArray()) {
        if (c == '(' || c == '[' || c == '{') {
            stack.push(c);
        } else {
            if (stack.isEmpty()) return false;
            char top = stack.pop();
            if (c == ')' && top != '(') return false;
            if (c == ']' && top != '[') return false;
            if (c == '}' && top != '{') return false;
        }
    }
    return stack.isEmpty();
}

🎯 When to Use Each

Stack Problems

  • Valid parentheses/brackets
  • Function call management
  • Undo operations
  • DFS traversal (implicit via recursion)
  • Expression evaluation

Queue Problems

  • BFS traversal
  • Level-order tree traversal
  • Task scheduling
  • Sliding window problems
  • Buffer for streams

Java Best Practices

Use ArrayDeque, not Stack class - Vector-based Stack is legacy and slower
Use ArrayDeque for Queue interface - LinkedList has overhead
Know your operations - push/pop vs offer/poll naming
Check isEmpty() before pop/poll - avoid exceptions

Graphs

Graphs are everywhere in coding interviews. Master BFS/DFS and representation, and you’ll solve most graph problems with confidence.

📊 Graph Representations

Edge List

List of all edges: [[0,1], [1,2]]

Simple but inefficient for queries

Adjacency List

Map: node → neighbors list

Most common, memory efficient

Adjacency Matrix

2D array: matrix[i][j] = edge

Fast lookups, high memory

🎯 Adjacency List (Recommended)

Always use this version - works with any node type, no compilation issues.

// Adjacency List - ALWAYS use this version
Map<Integer, List<Integer>> adjList = new HashMap<>() {
    {
        put(0, Arrays.asList(1));
        put(1, Arrays.asList(0, 2, 3));
        put(2, Arrays.asList(1, 3));
        put(3, Arrays.asList(1, 2));
    }
};

⚖️ Weighted Graphs

For problems involving costs, distances, or weights (e.g., currency exchange).

// Weighted Graph with Double weights
Map<String, Map<String, Double>> adjList = new HashMap<>();
Map<String, Double> edges = new HashMap<>();
edges.put("USD", 990.0);
edges.put("EUR", 1150.0);
adjList.put("BTC", edges);
// BTC -> USD has weight 990.0

🔍 BFS Template

The bread and butter of graph problems - memorize this pattern.

// BFS Template for graph problems
Queue<Integer> queue = new ArrayDeque<>();
Set<Integer> visited = new HashSet<>();

queue.offer(startNode);
visited.add(startNode);

while (!queue.isEmpty()) {
    int node = queue.poll();

    for (int neighbor : adjList.get(node)) {
        if (!visited.contains(neighbor)) {
            visited.add(neighbor);
            queue.offer(neighbor);
        }
    }
}

🎯 Common Graph Patterns

Path between nodes?

→ Run BFS/DFS, see if you can reach target

Shortest path (unweighted)?

→ BFS from source to target, then backtrack

Can color with two colors?

→ BFS + assign colors, abort if conflict (bipartite check)

Has cycle (undirected)?

→ BFS + visited tracking, cycle if node visited twice

Advanced: For weighted graphs, use Dijkstra. For topological ordering, use DFS + stack.

Heaps & Priority Queues

Heaps are perfect for “K largest/smallest” problems. The key insight: when you see K + min/max, think heap immediately.

🔢 Basic Heap Types

MinHeap (Default)

// MinHeap - default ordering (ascending)
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
minHeap.offer(5);
minHeap.offer(1); 
minHeap.offer(3);
// poll() returns: 1, 3, 5

MaxHeap

// MaxHeap - reverse ordering (descending)  
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
maxHeap.offer(5);
maxHeap.offer(1);
maxHeap.offer(3);
// poll() returns: 5, 3, 1

🎯 Custom Comparator

For complex objects like points, entries, or custom sorting logic.

// Custom comparator for complex objects
PriorityQueue<int[]> pq = new PriorityQueue<>(new Comparator<int[]>() {
    public int compare(int[] p1, int[] p2) {
        // Compare by distance for K-closest points problem
        double d1 = Math.sqrt(p1[0] * p1[0] + p1[1] * p1[1]);
        double d2 = Math.sqrt(p2[0] * p2[0] + p2[1] * p2[1]);
        return Double.compare(d1, d2); // min-heap by distance
    }
});

🏆 Top K Pattern (Most Important!)

Counter-intuitive but crucial: Use the OPPOSITE heap type!

// Top K Pattern - use OPPOSITE heap!
// For Top K smallest → use MAX heap (size K)
// For Top K largest → use MIN heap (size K)

PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
for (int num : nums) {
    maxHeap.offer(num);
    if (maxHeap.size() > k) {
        maxHeap.poll(); // remove largest element
    }
}
// Heap now contains K smallest elements
// Runtime: O(N log K) instead of O(N log N)

💡 Heap Problem Recognition

When you see “K largest/smallest”

→ Immediately think heap with size K

Runtime optimization

→ O(N log K) vs O(N log N) - huge difference for large N!

Space optimization

→ Heap size limited to K instead of storing all N elements

🎪 Common Heap Problems

Top K Frequent Elements - Min heap with frequency
K Closest Points - Max heap with distance
Merge K Sorted Lists - Min heap for smallest elements
Find Median - Two heaps (max + min)
Sliding Window Maximum - Deque or heap
Course Schedule III - Max heap for time management

Sorting Algorithms

Sorting is fundamental to computer science. Understanding stability, time complexity, and when to use each algorithm is crucial for technical interviews.

🔄 What is Stable Sorting?

A sorting algorithm is stable when elements with equal keys maintain their relative order from the original sequence.

Input: ["apple", "ark", "zoo", "free", "frisbee"]
Sort by: First character only
Output: ["apple", "ark", "free", "frisbee", "zoo"]
Notice “free” comes before “frisbee” (original order preserved)

📊 Sorting Algorithm Comparison

AlgorithmTime (Avg)Time (Worst)SpaceStable?
Bubble SortO(n²)O(n²)O(1)
Quick SortO(n log n)O(n²)O(log n)
Merge SortO(n log n)O(n log n)O(n)
Counting SortO(n + k)O(n + k)O(n + k)

🔢 Counting Sort Implementation

Perfect for small integer ranges. Stable, O(n + k) time complexity.

// Counting Sort - O(n + k) time, stable algorithm
int[] A = {1, 4, 1, 2, 7, 5, 2};

// Step 1: Count frequencies
int[] counts = new int[A.length + 1];
for (int i : A) {
    counts[i]++;
}

// Step 2: Transform to starting indices
for (int i = 1; i < counts.length; i++) {
    counts[i] = counts[i] + counts[i - 1];
}

// Step 3: Make it stable (work backwards)
for (int i = counts.length - 1; i >= 0; i--) {
    if (i == 0) {
        counts[i] = 0;
    } else {
        counts[i] = counts[i - 1];
    }
}

// Step 4: Build sorted array
int[] sorted = new int[A.length];
for (int i = 0; i < A.length; i++) {
    int num = A[i];
    int index = counts[num];
    sorted[index] = num;
    counts[num]++; // increment for next occurrence
}

🏳️ Dutch National Flag (Sort Colors)

Classic 3-way partitioning problem - sort 0s, 1s, 2s in one pass.

// Sort Colors - Dutch National Flag Problem
public void sortColors(int[] nums) {
    int zeroIndex = 0;      // boundary for 0s
    int twoIndex = nums.length - 1; // boundary for 2s
    int i = 0;

    // Process all elements before twoIndex
    while (i <= twoIndex) {
        if (nums[i] == 0) {
            swap(nums, i, zeroIndex);
            zeroIndex++;
            i++; // safe to advance after placing 0
        } else if (nums[i] == 2) {
            swap(nums, i, twoIndex);
            twoIndex--;
            // Don't advance i - need to check swapped element
        } else {
            i++; // nums[i] == 1, already in correct region
        }
    }
}

💡 Interview Sorting Tips

  • Small, known range? → Use Counting Sort O(n + k)
  • Need stability? → Merge Sort over Quick Sort
  • Memory constrained? → Quick Sort or Heap Sort
  • Nearly sorted data? → Insertion Sort (adaptive)
  • Custom comparator? → Use Arrays.sort() with Comparator

Pro tip: Radix Sort uses Counting Sort as a subroutine for multi-digit numbers!

Your Algorithm Toolbox

You’ve mastered the fundamentals. Now go crush those technical interviews! Remember: Pattern Recognition + Muscle Memory = Success

🎯 Data Structures Mastered

Arrays & ListsHash MapsStacks & QueuesTrees & GraphsHeapsLinked Lists

⚡ Algorithm Patterns

Two PointersBFS & DFSBinary SearchDynamic ProgrammingSortingSliding Window

Ready to Ace Your Interviews? 🚀

You’ve got the patterns, the code templates, and the confidence. Time to tackle those LeetCode problems and land your dream role!