Leetcode Patterns

1. Sliding Window Pattern

Used when you need to inspect subarrays or substrings that involve consecutive elements.

Problem: Find the maximum sum of a subarray of size 'k'

class Solution {
public:
    int maxSumSubarray(vector<int>& nums, int k) {
        int maxSum = 0, windowSum = 0;
        for (int i = 0; i < k; i++) {
            windowSum += nums[i];
        }
        maxSum = windowSum;

        for (int i = k; i < nums.size(); i++) {
            windowSum += nums[i] - nums[i - k];
            maxSum = max(maxSum, windowSum);
        }
        return maxSum;
    }
};

2. Two Pointers Pattern

Efficient for problems involving pairs of elements or comparisons within an array.

Problem: Find two numbers in a sorted array that add up to a target sum.

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int left = 0, right = numbers.size() - 1;
        while (left < right) {
            int sum = numbers[left] + numbers[right];
            if (sum == target) {
                return {left + 1, right + 1};
            } else if (sum < target) {
                left++;  // move the left pointer to increase the sum
            } else {
                right--;  // move the right pointer to decrease the sum
            }
        }
        return {};
    }
};

3. Fast and Slow Pointers (Tortoise and Hare)

Used to detect cycles in linked lists or arrays.

Problem: Detect if a linked list has a cycle.

class Solution {
public:
    bool hasCycle(ListNode *head) {
        ListNode *slow = head, *fast = head;
        while (fast && fast->next) {
            slow = slow->next;
            fast = fast->next->next;
            if (slow == fast) {
                return true; // cycle detected
            }
        }
        return false;
    }
};

4. Merge Intervals Pattern

Used when working with overlapping intervals, such as scheduling.

Problem: Merge overlapping intervals.

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end());  // sort by start time
        vector<vector<int>> merged;

        for (const auto& interval : intervals) {
            if (merged.empty() || merged.back()[1] < interval[0]) {
                merged.push_back(interval);  // no overlap, add the interval
            } else {
                merged.back()[1] = max(merged.back()[1], interval[1]);  // merge intervals
            }
        }
        return merged;
    }
};

5. Binary Search Pattern

Ideal when searching for a target in a sorted array or a monotonic function.

Problem: Find the first position of a target in a sorted array.

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        return -1;
    }
};

6. Backtracking Pattern

Used for problems that involve exploring all possible solutions, such as permutations, combinations, or subsets.

Problem: Generate all subsets of a set.

class Solution {
public:
    void backtrack(vector<int>& nums, vector<vector<int>>& result, vector<int>& subset, int index) {
        result.push_back(subset);  // add current subset to the result
        for (int i = index; i < nums.size(); i++) {
            subset.push_back(nums[i]);  // include nums[i]
            backtrack(nums, result, subset, i + 1);  // recursively explore further
            subset.pop_back();  // exclude nums[i], backtrack to previous state
        }
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> result;
        vector<int> subset;
        backtrack(nums, result, subset, 0);
        return result;
    }
};

7. Dynamic Programming (DP) Pattern

Used when a problem can be broken down into overlapping subproblems, with optimal substructure.

Problem: Solve the 0/1 Knapsack problem.

class Solution {
public:
    int knapsack(vector<int>& weights, vector<int>& values, int W) {
        int n = weights.size();
        vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));

        for (int i = 1; i <= n; i++) {
            for (int w = 1; w <= W; w++) {
                if (weights[i - 1] <= w) {
                    dp[i][w] = max(dp[i - 1][w], values[i - 1] + dp[i - 1][w - weights[i - 1]]);
                } else {
                    dp[i][w] = dp[i - 1][w];
                }
            }
        }
        return dp[n][W];
    }
};

8. Greedy Pattern

Used when making local optimal choices at each step leads to a global optimal solution.

Problem: Find the minimum number of coins to make up a target amount.

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        sort(coins.rbegin(), coins.rend());  // sort coins in descending order
        int count = 0;
        for (int coin : coins) {
            if (amount >= coin) {
                count += amount / coin;  // use as many large coins as possible
                amount %= coin;
            }
        }
        return amount == 0 ? count : -1;  // return -1 if the amount cannot be made
    }
};

9. Topological Sort (Graph Traversal)

Used for problems involving dependency resolution, such as scheduling tasks.

Problem: Find the order of courses to take given prerequisites.

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int> indegree(numCourses, 0);
        vector<vector<int>> graph(numCourses);

        for (const auto& pre : prerequisites) {
            graph[pre[1]].push_back(pre[0]);
            indegree[pre[0]]++;
        }

        queue<int> q;
        for (int i = 0; i < numCourses; i++) {
            if (indegree[i] == 0) q.push(i);  // start with courses having no prerequisites
        }

        vector<int> order;
        while (!q.empty()) {
            int course = q.front();
            q.pop();
            order.push_back(course);
            for (int next : graph[course]) {
                if (--indegree[next] == 0) {
                    q.push(next);
                }
            }
        }

        return order.size() == numCourses ? order : vector<int>();  // return an empty array if a cycle exists
    }
};

10. Union-Find (Disjoint Set Union) Pattern

Used for problems involving connected components in graphs.

Problem: Detect if a graph contains a cycle.

class Solution {
public:
    int findParent(vector<int>& parent, int node) {
        if (parent[node] != node) {
            parent[node] = findParent(parent, parent[node]);  // path compression
        }
        return parent[node];
    }

    bool unionNodes(vector<int>& parent, vector<int>& rank, int u, int v) {
        int rootU = findParent(parent, u);
        int rootV = findParent(parent, v);
        if (rootU == rootV) return false;  // cycle detected

        if (rank[rootU] > rank[rootV]) {
            parent[rootV] = rootU;
        } else if (rank[rootU] < rank[rootV]) {
            parent[rootU] = rootV;
        } else {
            parent[rootV] = rootU;
            rank[rootU]++;
        }
        return true;
    }

    bool hasCycle(int n, vector<vector<int>>& edges) {
        vector<int> parent(n), rank(n, 0);
        iota(parent.begin(), parent.end(), 0);  // initialize each node as its own parent

        for (const auto& edge : edges) {
            if (!unionNodes(parent, rank, edge[0], edge[1])) {
                return true;  // cycle found
            }
        }
        return false;  // no cycle
    }
};

11. Cyclic Sort Pattern

When to Use: When you're dealing with a range of numbers from 1 to n or 0 to n, and you're asked to rearrange or find missing/duplicate numbers.

Code Idea:

vector<int> cyclicSort(vector<int>& nums) {
    int i = 0;
    while (i < nums.size()) {
        int correctIndex = nums[i] - 1;
        if (nums[i] != nums[correctIndex]) {
            swap(nums[i], nums[correctIndex]);
        } else {
            i++;
        }
    }
    return nums;
}

Problems:

  • Find the Missing Number
  • Find All Duplicates in an Array
  • Set Mismatch

12. BFS/DFS Pattern

When to Use:

When you're working with graphs or trees (or matrix as a grid).

Data structures and Algorithms

  • Use DFS for deep traversal.
  • Use BFS for shortest path in unweighted graphs.

DFS Code (Recursive):

void dfs(int node, vector<vector<int>>& graph, vector<bool>& visited) {
    visited[node] = true;
    for (int neighbor : graph[node]) {
        if (!visited[neighbor])
            dfs(neighbor, graph, visited);
    }
}

BFS Code:

void bfs(int start, vector<vector<int>>& graph) {
    queue<int> q;
    vector<bool> visited(graph.size(), false);
    q.push(start);
    visited[start] = true;

    while (!q.empty()) {
        int node = q.front(); q.pop();
        for (int neighbor : graph[node]) {
            if (!visited[neighbor]) {
                visited[neighbor] = true;
                q.push(neighbor);
            }
        }
    }
}

Problems:

  • Clone Graph
  • Number of Provinces
  • Word Ladder

13. Island (Matrix traversal) Pattern

When to Use: When you are given a 2D grid and need to count or explore connected components.

DFS in Matrix:

void dfs(vector<vector<char>>& grid, int r, int c) {
    if (r < 0 || c < 0 || r >= grid.size() || c >= grid[0].size() || grid[r][c] == '0')
        return;

    grid[r][c] = '0'; // mark visited

    dfs(grid, r+1, c);
    dfs(grid, r-1, c);
    dfs(grid, r, c+1);
    dfs(grid, r, c-1);
}

Problems:

  • Number of Islands
  • Surrounded Regions
  • Flood Fill

14. In-place Reversal of Linked List

When to Use: To reverse a linked list or part of it, especially in O(1) space.

Code:

ListNode* reverseList(ListNode* head) {
    ListNode* prev = NULL;
    while (head) {
        ListNode* nextNode = head->next;
        head->next = prev;
        prev = head;
        head = nextNode;
    }
    return prev;
}

Problems:

  • Reverse Linked List
  • Reverse Nodes in k-Group
  • Palindrome Linked List

15. Two Heaps Pattern

When to Use: Used in problems where you need to find median, or maintain order statistics dynamically.

DFS in Matrix:

priority_queue<int> maxHeap; // for left half
priority_queue<int, vector<int>, greater<int>> minHeap; // for right half

void addNum(int num) {
    if (maxHeap.empty() || num <= maxHeap.top()) maxHeap.push(num);
    else minHeap.push(num);

    // balance
    if (maxHeap.size() > minHeap.size() + 1) {
        minHeap.push(maxHeap.top()); maxHeap.pop();
    } else if (minHeap.size() > maxHeap.size()) {
        maxHeap.push(minHeap.top()); minHeap.pop();
    }
}

Problems:

  • Median of Data Stream
  • Sliding Window Median

16. Modified Binary Search Pattern

When to Use: When you do binary search not on the values, but on a condition, index, or to solve min/max problems.

Code:

int binarySearch(vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;
    while (left <= right) {
        int mid = left + (right-left)/2;
        if (nums[mid] == target) return mid;
        else if (nums[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}

Modified use case: Find smallest number ≥ target.

Problems:

  • Search in Rotated Array
  • Peak Element
  • Koko Eating Bananas

17. Top K Elements

When you need to find k largest/smallest elements or frequent elements.

Code:

vector<int> topKFrequent(vector<int>& nums, int k) {
    unordered_map<int, int> freq;
    for (int num : nums) freq[num]++;

    priority_queue<pair<int, int>> maxHeap;
    for (auto& [num, count] : freq)
        maxHeap.push({count, num});

    vector<int> res;
    while (k--) {
        res.push_back(maxHeap.top().second);
        maxHeap.pop();
    }
    return res;
}

Problems:

  • Top K Frequent Elements
  • K Closest Numbers
  • Top K Frequent Words

18. 0/1 Knapsack Pattern

Used in DP when choosing to either include or exclude an item.

Code:

int knapsack(vector<int>& weight, vector<int>& value, int W) {
    int n = weight.size();
    vector<vector<int>> dp(n+1, vector<int>(W+1, 0));

    for (int i=1; i<=n; i++) {
        for (int w=0; w<=W; w++) {
            if (weight[i-1] <= w)
                dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight[i-1]] + value[i-1]);
            else
                dp[i][w] = dp[i-1][w];
        }
    }
    return dp[n][W];
}

Problems:

  • Subset Sum
  • Partition Equal Subset Sum
  • Target Sum

19. Bitwise XOR

When you need to cancel out duplicate values or perform toggling.

Code:

int findSingleNumber(vector<int>& nums) {
    int res = 0;
    for (int num : nums) res ^= num;
    return res;
}

Problems:

  • Single Number
  • Find Missing Number
  • XOR of All Subsets

20. K-way Merge

When merging k sorted arrays or lists.

Code:

struct Compare {
    bool operator()(pair<int, pair<int, int>>& a, pair<int, pair<int, int>>& b) {
        return a.first > b.first;
    }
};

vector<int> mergeKSortedLists(vector<vector<int>>& lists) {
    priority_queue<pair<int, pair<int, int>>, vector<pair<int, pair<int, int>>>, Compare> pq;
    vector<int> indices(lists.size(), 0), result;

    for (int i = 0; i < lists.size(); i++) {
        if (!lists[i].empty())
            pq.push({lists[i][0], {i, 0}});
    }

    while (!pq.empty()) {
        auto [val, pos] = pq.top(); pq.pop();
        result.push_back(val);
        int i = pos.first, j = pos.second + 1;
        if (j < lists[i].size()) pq.push({lists[i][j], {i, j}});
    }

    return result;
}

Problems:

  • Merge K Sorted Lists
  • Smallest Range Covering Elements from K Lists

21. Monotonic Stack

Used to find next/previous greater/smaller elements

Code:

vector<int> nextGreater(vector<int>& nums) {
    vector<int> res(nums.size(), -1);
    stack<int> st;

    for (int i = 0; i < nums.size(); i++) {
        while (!st.empty() && nums[i] > nums[st.top()]) {
            res[st.top()] = nums[i];
            st.pop();
        }
        st.push(i);
    }

    return res;
}

Problems:

  • Daily Temperatures
  • Largest Rectangle in Histogram
  • Next Greater Element

22. Multi-threaded

When optimizing tasks with parallelism or for handling concurrent operations (not core DSA but used in system design/interviews).

Example Idea (Java):

class MyRunnable implements Runnable {
    public void run() {
        System.out.println("Thread is running");
    }
}

Problems:

  • Multi-threaded FizzBuzz
  • Building H2O
  • Print in Order

23. Kth Smallest/Largest Element

Asked directly to find k-th smallest/largest, can use heaps or quickselect.

Heap Code:

int findKthLargest(vector<int>& nums, int k) {
    priority_queue<int, vector<int>, greater<int>> minHeap;

    for (int num : nums) {
        minHeap.push(num);
        if (minHeap.size() > k) minHeap.pop();
    }
    return minHeap.top();
}

Problems:

  • Kth Smallest Element in BST
  • Kth Largest Element in Array
  • Find K Closest Elements