Skip to content
Algorithms Deep Dive: Sorting, Searching, Dynamic Programming, and Graph Patterns

Algorithms Deep Dive: Sorting, Searching, Dynamic Programming, and Graph Patterns

DodaTech Updated Jun 20, 2026 9 min read

Algorithms are step-by-step procedures for solving computational problems — and recognizing which algorithm category a problem belongs to is the key to solving it efficiently under interview pressure.

What You’ll Learn

  • Sorting algorithms: quicksort, mergesort, heapsort — implementations and when to use each
  • Binary search patterns and variations
  • Dynamic programming: top-down vs bottom-up, common patterns
  • Graph algorithms: shortest path, MST, topological sort
  • How to recognize algorithm patterns from problem descriptions

Why Algorithms Matter

Coding interviews exist because they test your ability to reason about efficiency, recognize problem patterns, and implement non-trivial logic under time pressure. Companies like Google, Meta, and Microsoft use algorithm questions because they correlate with strong engineering skills. Mastering the common algorithm patterns turns unfamiliar problems into recognizable templates.

DodaZIP uses sorting and compression algorithms that must balance speed and memory — choosing the right algorithm directly affects user experience.

Learning Path

    flowchart LR
  A[Data Structures Deep] --> B[Algorithms Deep<br/>You are here]
  B --> C[System Design]
  C --> D[Mock Interviews]
  D --> E[Offer Negotiation]
  style B fill:#f90,color:#fff
  

Sorting

Quicksort

def quicksort(arr, low=0, high=None):
    if high is None:
        high = len(arr) - 1
    if low < high:
        pivot = partition(arr, low, high)
        quicksort(arr, low, pivot - 1)
        quicksort(arr, pivot + 1, high)
    return arr

def partition(arr, low, high):
    pivot = arr[high]
    i = low - 1
    for j in range(low, high):
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]
    arr[i + 1], arr[high] = arr[high], arr[i + 1]
    return i + 1

# Average: O(n log n), Worst: O(n²), Space: O(log n)

Mergesort

def mergesort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left = mergesort(arr[:mid])
    right = mergesort(arr[mid:])
    return merge(left, right)

def merge(left, right):
    result = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    result.extend(left[i:])
    result.extend(right[j:])
    return result

# Time: O(n log n), Space: O(n)

Heapsort

def heapsort(arr):
    n = len(arr)

    # Build max heap
    for i in range(n // 2 - 1, -1, -1):
        heapify(arr, n, i)

    # Extract elements
    for i in range(n - 1, 0, -1):
        arr[0], arr[i] = arr[i], arr[0]
        heapify(arr, i, 0)
    return arr

def heapify(arr, n, i):
    largest = i
    left = 2 * i + 1
    right = 2 * i + 2
    if left < n and arr[left] > arr[largest]:
        largest = left
    if right < n and arr[right] > arr[largest]:
        largest = right
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)

# Time: O(n log n), Space: O(1)

Sorting Cheatsheet

AlgorithmAverageWorstSpaceStable
QuicksortO(n log n)O(n²)O(log n)No
MergesortO(n log n)O(n log n)O(n)Yes
HeapsortO(n log n)O(n log n)O(1)No
BubbleO(n²)O(n²)O(1)Yes
InsertionO(n²)O(n²)O(1)Yes

Binary Search

Classic Binary Search

def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

Binary Search Variations

# Find first occurrence (lower bound)
def first_occurrence(arr, target):
    left, right = 0, len(arr) - 1
    result = -1
    while left <= right:
        mid = left + (right - left) // 2
        if arr[mid] >= target:
            right = mid - 1
        else:
            left = mid + 1
        if arr[mid] == target:
            result = mid
    return result

# Find last occurrence (upper bound)
def last_occurrence(arr, target):
    left, right = 0, len(arr) - 1
    result = -1
    while left <= right:
        mid = left + (right - left) // 2
        if arr[mid] <= target:
            left = mid + 1
        else:
            right = mid - 1
        if arr[mid] == target:
            result = mid
    return result

# Search in rotated sorted array
def search_rotated(nums, target):
    left, right = 0, len(nums) - 1
    while left <= right:
        mid = left + (right - left) // 2
        if nums[mid] == target:
            return mid
        if nums[left] <= nums[mid]:  # Left half is sorted
            if nums[left] <= target < nums[mid]:
                right = mid - 1
            else:
                left = mid + 1
        else:  # Right half is sorted
            if nums[mid] < target <= nums[right]:
                left = mid + 1
            else:
                right = mid - 1
    return -1

Dynamic Programming

Top-Down (Memoization)

from functools import lru_cache

# Fibonacci with memoization
@lru_cache(maxsize=None)
def fib(n):
    if n <= 1:
        return n
    return fib(n - 1) + fib(n - 2)

# Time: O(n), Space: O(n)

Bottom-Up (Tabulation)

def fib_bottom_up(n):
    if n <= 1:
        return n
    dp = [0] * (n + 1)
    dp[1] = 1
    for i in range(2, n + 1):
        dp[i] = dp[i - 1] + dp[i - 2]
    return dp[n]

# Time: O(n), Space: O(n) → O(1) with space optimization
def fib_optimized(n):
    if n <= 1:
        return n
    prev2, prev1 = 0, 1
    for _ in range(2, n + 1):
        current = prev1 + prev2
        prev2 = prev1
        prev1 = current
    return prev1

Common DP Patterns

# 0/1 Knapsack
def knapsack(weights, values, capacity):
    n = len(weights)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for w in range(1, capacity + 1):
            if weights[i - 1] <= w:
                dp[i][w] = max(
                    values[i - 1] + dp[i - 1][w - weights[i - 1]],
                    dp[i - 1][w]
                )
            else:
                dp[i][w] = dp[i - 1][w]
    return dp[n][capacity]

# Longest Common Subsequence
def lcs(text1, text2):
    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]

# Coin Change (minimum coins)
def coin_change(coins, amount):
    dp = [float('inf')] * (amount + 1)
    dp[0] = 0
    for coin in coins:
        for a in range(coin, amount + 1):
            dp[a] = min(dp[a], dp[a - coin] + 1)
    return dp[amount] if dp[amount] != float('inf') else -1

DP Pattern Recognition

PatternExample ProblemState Definition
Fibonacci-styleClimbing stairsdp[n] = dp[n-1] + dp[n-2]
0/1 KnapsackSubset sumdp[i][w] = max value with first i items, weight w
Unbounded KnapsackCoin changedp[a] = min coins to make amount a
LCSEdit distancedp[i][j] = LCS of first i chars and first j chars
LISLongest increasing subsequencedp[i] = LIS ending at index i
MatrixPath in griddp[i][j] = ways to reach (i,j) from start

Graph Algorithms

Dijkstra (Shortest Path)

import heapq

def dijkstra(graph, start):
    distances = {node: float('inf') for node in graph}
    distances[start] = 0
    pq = [(0, start)]

    while pq:
        current_dist, current = heapq.heappop(pq)
        if current_dist > distances[current]:
            continue
        for neighbor, weight in graph[current]:
            distance = current_dist + weight
            if distance < distances[neighbor]:
                distances[neighbor] = distance
                heapq.heappush(pq, (distance, neighbor))
    return distances

# Time: O((V + E) log V)

Topological Sort

from collections import deque

def topological_sort(graph):
    in_degree = {node: 0 for node in graph}
    for node in graph:
        for neighbor in graph[node]:
            in_degree[neighbor] = in_degree.get(neighbor, 0) + 1

    queue = deque([n for n, d in in_degree.items() if d == 0])
    result = []

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

    return result if len(result) == len(graph) else []

Common Algorithm Mistakes

1. Not Recognizing the Pattern

Spending 20 minutes on a DP problem when it’s actually a greedy problem.

Fix: Before coding, ask: “Can I use two pointers? Does this have optimal substructure?”

2. Off-by-One Errors

Binary search and DP array indices are common off-by-one sources.

Fix: Test with small examples. Check array bounds on every access.

3. Wrong Base Cases

DP solutions fail when base cases are wrong or missing.

Fix: Write base cases for: 0, 1, empty input, and minimal non-trivial input.

4. Forgetting to Sort

Many problems require sorted input (binary search, two-pointer, intervals).

Fix: “Do I need sorted input?” — if yes, sort first.

5. Infinite Loops

Graph algorithms with cycles (DFS, topological sort) can loop infinitely.

Fix: Use visited sets. For topological sort, track in-degrees correctly.

6. Suboptimal Algorithm

Dijkstra for unweighted graphs (use BFS). BFS for weighted graphs with negative edges (use Bellman-Ford).

Fix: Match the algorithm to the problem constraints.

7. Not Analyzing Complexity

Solving a problem but not understanding why it’s fast or slow.

Fix: Always state time and space complexity before and after coding.

Practice Questions

1. What is the difference between mergesort and quicksort?

Mergesort is O(n log n) guaranteed, stable, but uses O(n) space. Quicksort is O(n log n) average, O(n²) worst, in-place, and unstable.

2. What is dynamic programming and when do you use it?

DP solves problems with overlapping subproblems and optimal substructure by breaking them into simpler subproblems and storing results.

3. What is the difference between Dijkstra and BFS for shortest path?

BFS works on unweighted graphs (shortest path by edge count). Dijkstra works on weighted graphs with non-negative weights.

4. How do you recognize a DP problem?

It asks for optimal value (min, max, longest, shortest), has overlapping subproblems, and the brute-force is exponential.

5. What is topological sort and when is it used?

Ordering nodes in a directed acyclic graph so every edge goes from earlier to later. Used for dependency resolution, build systems, course scheduling.

Challenge: Implement a solution for the “Word Ladder” problem (LeetCode 127). Use BFS for shortest transformation path. Analyze time and space complexity.

FAQ

How many algorithms do I need to know for interviews?
Know sorting (quick, merge, heap), binary search, BFS/DFS, Dijkstra, and common DP patterns. These cover 90% of interview problems.
Should I memorize algorithm implementations?
Understand the logic and key steps. You should be able to write binary search, BFS, and DFS from memory. Other algorithms you can reconstruct from first principles.
How do I practice algorithm recognition?
Categorize every problem you solve by algorithm type. After 50+ problems, you’ll naturally recognize patterns.
What is the most common interview algorithm?
Binary search variations — they appear in problems that don’t look like searching at first glance.
Do senior roles require harder algorithms?
Senior roles focus more on system design but still expect algorithmic competency at the mid-level. The algorithms don’t get harder, but the expectation for clean, efficient code increases.

What’s Next

TutorialWhat You’ll Learn
System Design Interview PrepDesigning scalable systems
Interview Questions BankCurated list of 50+ practice questions
Mock Interview PracticeFull-length interview simulations

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro. Updated 2026-06-20.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro