Top 10 DSA Patterns Every Indian Developer Must Know

Why Patterns Matter More Than Problems

The biggest mistake developers make in DSA preparation is grinding LeetCode randomly. With over 3,000 problems on the platform, brute-force practice is impossibly inefficient. The secret that consistently separates candidates who clear interviews at Google, Amazon, Flipkart, and Razorpay from those who don’t is pattern recognition. There are roughly 10-15 core patterns that cover 90% of coding interview questions. Learn the pattern, and you can solve any problem that fits it.

This guide covers the 10 patterns that appear most frequently in Indian tech interviews, with Python implementations you can adapt and apply immediately.

1. Two Pointers

Use when working with sorted arrays or when you need to find pairs that satisfy a condition. Two pointers move toward each other or in the same direction, reducing O(n^2) brute force to O(n).

ADVERTISEMENT
def two_sum_sorted(nums: list[int], target: int) -> list[int]:
    """Find two numbers in a sorted array that sum to target."""
    left, right = 0, len(nums) - 1

    while left < right:
        current_sum = nums[left] + nums[right]
        if current_sum == target:
            return [left, right]
        elif current_sum < target:
            left += 1
        else:
            right -= 1

    return [-1, -1]


def remove_duplicates(nums: list[int]) -> int:
    """Remove duplicates in-place from sorted array. Returns new length."""
    if not nums:
        return 0

    write = 1
    for read in range(1, len(nums)):
        if nums[read] != nums[read - 1]:
            nums[write] = nums[read]
            write += 1

    return write

Key problems: Container With Most Water, 3Sum, Trapping Rain Water, Remove Duplicates.

2. Sliding Window

Use for problems involving contiguous subarrays or substrings. Maintain a window that expands or shrinks based on conditions.

def max_sum_subarray(nums: list[int], k: int) -> int:
    """Maximum sum of any subarray of size k (fixed window)."""
    window_sum = sum(nums[:k])
    max_sum = window_sum

    for i in range(k, len(nums)):
        window_sum += nums[i] - nums[i - k]
        max_sum = max(max_sum, window_sum)

    return max_sum


def longest_substring_without_repeat(s: str) -> int:
    """Longest substring without repeating characters (variable window)."""
    char_index = {}
    max_length = 0
    left = 0

    for right, char in enumerate(s):
        if char in char_index and char_index[char] >= left:
            left = char_index[char] + 1
        char_index[char] = right
        max_length = max(max_length, right - left + 1)

    return max_length

Key problems: Minimum Window Substring, Longest Repeating Character Replacement, Maximum Average Subarray.

3. Binary Search

Not just for sorted arrays. Use binary search whenever you can define a monotonic condition that partitions the search space.

def search_rotated(nums: list[int], target: int) -> int:
    """Search in a rotated sorted array. O(log n)."""
    left, right = 0, len(nums) - 1

    while left <= right:
        mid = (left + right) // 2

        if nums[mid] == target:
            return mid

        # Left half is sorted
        if nums[left] <= nums[mid]:
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1
        # Right half is sorted
        else:
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1

    return -1


def find_peak_element(nums: list[int]) -> int:
    """Find a peak element index. O(log n)."""
    left, right = 0, len(nums) - 1

    while left < right:
        mid = (left + right) // 2
        if nums[mid] < nums[mid + 1]:
            left = mid + 1
        else:
            right = mid

    return left

Key problems: Koko Eating Bananas, Find Minimum in Rotated Array, Median of Two Sorted Arrays.

4. BFS and DFS (Graph/Tree Traversal)

BFS explores level by level (use a queue). DFS goes deep first (use recursion or a stack). These are the backbone of tree and graph problems.

from collections import deque

def level_order_traversal(root) -> list[list[int]]:
    """BFS level-order traversal of a binary tree."""
    if not root:
        return []

    result = []
    queue = deque([root])

    while queue:
        level = []
        for _ in range(len(queue)):
            node = queue.popleft()
            level.append(node.val)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        result.append(level)

    return result


def num_islands(grid: list[list[str]]) -> int:
    """Count islands using DFS. Classic graph problem."""
    if not grid:
        return 0

    rows, cols = len(grid), len(grid[0])
    count = 0

    def dfs(r, c):
        if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] == "0":
            return
        grid[r][c] = "0"  # Mark visited
        dfs(r + 1, c)
        dfs(r - 1, c)
        dfs(r, c + 1)
        dfs(r, c - 1)

    for r in range(rows):
        for c in range(cols):
            if grid[r][c] == "1":
                count += 1
                dfs(r, c)

    return count

Key problems: Word Ladder (BFS), Clone Graph, Course Schedule, Rotting Oranges.

5. Dynamic Programming

Use when a problem has overlapping subproblems and optimal substructure. Start with recursion, add memoization, then convert to tabulation if needed.

def longest_common_subsequence(text1: str, text2: str) -> int:
    """Classic DP: LCS of two strings. O(m*n) time and space."""
    m, n = len(text1), len(text2)
    dp = [[0] * (n + 1) for _ in range(m + 1)]

    for i in range(1, m + 1):
        for j in range(1, n + 1):
            if text1[i - 1] == text2[j - 1]:
                dp[i][j] = dp[i - 1][j - 1] + 1
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])

    return dp[m][n]


def coin_change(coins: list[int], amount: int) -> int:
    """Minimum coins to make amount. Bottom-up DP."""
    dp = [float("inf")] * (amount + 1)
    dp[0] = 0

    for coin in coins:
        for x in range(coin, amount + 1):
            dp[x] = min(dp[x], dp[x - coin] + 1)

    return dp[amount] if dp[amount] != float("inf") else -1

Key problems: 0/1 Knapsack, Longest Increasing Subsequence, Edit Distance, House Robber.

6. Backtracking

Systematic exploration of all possibilities, pruning branches that can’t lead to a solution.

def combination_sum(candidates: list[int], target: int) -> list[list[int]]:
    """Find all unique combinations that sum to target."""
    result = []

    def backtrack(start, current, remaining):
        if remaining == 0:
            result.append(current[:])
            return
        if remaining < 0:
            return

        for i in range(start, len(candidates)):
            current.append(candidates[i])
            backtrack(i, current, remaining - candidates[i])
            current.pop()  # Undo choice

    backtrack(0, [], target)
    return result

Key problems: N-Queens, Sudoku Solver, Permutations, Word Search.

7. Hash Map / Frequency Counting

The most versatile pattern. Use hash maps to achieve O(1) lookups and reduce time complexity.

def group_anagrams(strs: list[str]) -> list[list[str]]:
    """Group strings that are anagrams of each other."""
    from collections import defaultdict

    groups = defaultdict(list)
    for s in strs:
        key = tuple(sorted(s))
        groups[key].append(s)

    return list(groups.values())


def subarray_sum_equals_k(nums: list[int], k: int) -> int:
    """Count subarrays with sum equal to k using prefix sum + hash map."""
    count = 0
    prefix_sum = 0
    prefix_counts = {0: 1}

    for num in nums:
        prefix_sum += num
        count += prefix_counts.get(prefix_sum - k, 0)
        prefix_counts[prefix_sum] = prefix_counts.get(prefix_sum, 0) + 1

    return count

8. Monotonic Stack

Use when you need to find the next greater or smaller element for each position in an array.

def next_greater_element(nums: list[int]) -> list[int]:
    """For each element, find the next greater element to its right."""
    n = len(nums)
    result = [-1] * n
    stack = []  # Stores indices

    for i in range(n):
        while stack and nums[i] > nums[stack[-1]]:
            result[stack.pop()] = nums[i]
        stack.append(i)

    return result

9. Merge Intervals

Sort intervals by start time, then merge overlapping ones.

def merge_intervals(intervals: list[list[int]]) -> list[list[int]]:
    """Merge all overlapping intervals."""
    intervals.sort(key=lambda x: x[0])
    merged = [intervals[0]]

    for start, end in intervals[1:]:
        if start <= merged[-1][1]:
            merged[-1][1] = max(merged[-1][1], end)
        else:
            merged.append([start, end])

    return merged

10. Topological Sort

Use for dependency resolution problems where you need to find a valid ordering.

def course_schedule(num_courses: int, prerequisites: list[list[int]]) -> list[int]:
    """Return a valid course order (topological sort using Kahn's algorithm)."""
    from collections import deque, defaultdict

    graph = defaultdict(list)
    in_degree = [0] * num_courses

    for course, prereq in prerequisites:
        graph[prereq].append(course)
        in_degree[course] += 1

    queue = deque(i for i in range(num_courses) if in_degree[i] == 0)
    order = []

    while queue:
        node = queue.popleft()
        order.append(node)
        for neighbor in graph[node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.append(neighbor)

    return order if len(order) == num_courses else []

Conclusion

These 10 patterns cover the vast majority of DSA questions you’ll encounter in interviews at Indian and global tech companies. The strategy is simple: learn each pattern deeply, solve 3-5 problems per pattern, and practice identifying which pattern fits a new problem before writing code. Pattern recognition is the skill that separates candidates who solve problems in 15 minutes from those who struggle for an hour. Build a spreadsheet mapping problems to patterns, revisit weak areas weekly, and you’ll walk into any coding interview with confidence.

ADVERTISEMENT

Leave a Comment

Your email address will not be published. Required fields are marked with an asterisk.