Sorting & Search Problems

Apply sorting as a first step to unlock two-pointer, binary search, and greedy patterns.

0 Problems2-3 hours

Overview

Many interview problems become trivial once you sort the input. Sorting enables two-pointer pairing, binary search lookups, interval merging, and greedy selection. The trade-off is an O(n log n) upfront cost, but it often eliminates the need for more complex data structures.

Complexity Summary

Time Complexity

Sorting O(n log n) · Then pattern-specific

Space Complexity

O(log n) for sort stack, O(1) for in-place sorts

Key Patterns & Techniques

Learn the core patterns in this topic. Each block explains when to use the pattern, the intuition behind it, and a compact code example.

1

Sort First to Unlock Structure

Concept

Use this when the original input order is irrelevant to the final answer

Why It Works

Sorting often reveals patterns that are hidden in the raw input, such as adjacency of duplicates, monotonic order, and easier pair selection. Many problems that look complicated at first become straightforward after sorting.

Pattern Example
Problem

Problem Merge Intervals. Sort intervals by start time so all overlaps become local and easy to process in one pass.

Implementation
// Sort first to unlock structure — Merge Intervals
function merge(intervals) {
  intervals.sort((a, b) => a[0] - b[0]);
  const result = [intervals[0]];
  for (let i = 1; i < intervals.length; i++) {
    const last = result[result.length - 1];
    if (intervals[i][0] <= last[1]) last[1] = Math.max(last[1], intervals[i][1]);
    else result.push([...intervals[i]]);
  }
  return result;
}
Practice questions for this pattern
2

Sort Then Two Pointers

Concept

Use this when you need to find pairs or triplets under a condition such as sum, distance, or closeness

Why It Works

Sorting allows the left and right pointers to move with purpose instead of guessing blindly. This is one of the most common reductions from brute force to efficient search.

Pattern Example
Problem

Problem 3Sum. Sort the array, fix one element, and use two pointers to find the remaining pair.

Implementation
// Sort then two pointers — 3Sum
function threeSum(nums) {
  nums.sort((a, b) => a - b);
  const result = [];
  for (let i = 0; i < nums.length - 2; i++) {
    if (i > 0 && nums[i] === nums[i-1]) continue;
    let left = i + 1, right = nums.length - 1;
    while (left < right) {
      const sum = nums[i] + nums[left] + nums[right];
      if (sum === 0) {
        result.push([nums[i], nums[left++], nums[right--]]);
        while (left < right && nums[left] === nums[left-1]) left++;
        while (left < right && nums[right] === nums[right+1]) right--;
      } else if (sum < 0) left++;
      else right--;
    }
  }
  return result;
}
Practice questions for this pattern
3

Comparator Thinking

Concept

Use this when objects have multiple fields and the sort order must reflect the exact problem logic

Why It Works

The sort rule is often part of the solution, not just a preprocessing step. Being precise here is critical because the rest of the algorithm assumes the chosen order is correct.

Pattern Example
Problem

Problem Merge Intervals. Sort by start time ascending so earlier intervals are processed before later ones.

Implementation
// Comparator thinking — Sort Colors (Dutch National Flag)
function sortColors(nums) {
  let low = 0, mid = 0, high = nums.length - 1;
  while (mid <= high) {
    if (nums[mid] === 0) { [nums[low], nums[mid]] = [nums[mid], nums[low]]; low++; mid++; }
    else if (nums[mid] === 1) { mid++; }
    else { [nums[mid], nums[high]] = [nums[high], nums[mid]]; high--; }
  }
}
Practice questions for this pattern
4

Duplicates Become Adjacent

Concept

Use this when you want to remove duplicates, skip repeated combinations, or compress equal values

Why It Works

Sorting brings equal elements next to each other, which lets you handle them with simple local checks instead of a hash set. This is especially useful in subset, permutation, and k-sum problems.

Pattern Example
Problem

Problem 3Sum. After sorting, skip nums[i] if it is the same as the previous value to avoid duplicate triplets.

Implementation
// Duplicates adjacent after sort — Contains Duplicate II
function containsNearbyDuplicate(nums, k) {
  const seen = new Map();
  for (let i = 0; i < nums.length; i++) {
    if (seen.has(nums[i]) && i - seen.get(nums[i]) <= k) return true;
    seen.set(nums[i], i);
  }
  return false;
}
Practice questions for this pattern
5

Partial Sorting with Selection

Concept

Use this when you only need the kth smallest or kth largest element, not a fully sorted array

Why It Works

QuickSelect applies the partition idea from quicksort but recurses only into the side containing the answer. This often improves the average runtime from O(n log n) to O(n).

Pattern Example
Problem

Problem Kth Largest Element in an Array. Partition around a pivot until the pivot lands on the target index.

Implementation
// Partial sorting with QuickSelect — Kth Largest Element
function findKthLargest(nums, k) {
  const target = nums.length - k;
  function quickSelect(lo, hi) {
    const pivot = nums[hi];
    let p = lo;
    for (let i = lo; i < hi; i++)
      if (nums[i] <= pivot) { [nums[i], nums[p]] = [nums[p], nums[i]]; p++; }
    [nums[p], nums[hi]] = [nums[hi], nums[p]];
    if (p === target) return nums[p];
    return p < target ? quickSelect(p + 1, hi) : quickSelect(lo, p - 1);
  }
  return quickSelect(0, nums.length - 1);
}
Practice questions for this pattern
6

Value-Bounded Counting Sort Idea

Concept

Use this when values lie in a small known range

Why It Works

Instead of comparison sorting, count how many times each value appears and rebuild the result. This can be linear time and is a useful alternative when the input domain is constrained.

Pattern Example
Problem

Problem Sort Colors. Count how many 0s, 1s, and 2s appear, then overwrite the array accordingly.

Implementation
// Counting sort idea — Sort Colors / Top K using bucket
function topKFrequent(nums, k) {
  const freq = new Map();
  for (const num of nums) freq.set(num, (freq.get(num) ?? 0) + 1);
  const buckets = Array.from({length: nums.length + 1}, () => []);
  for (const [num, count] of freq) buckets[count].push(num);
  const result = [];
  for (let i = buckets.length - 1; i >= 0 && result.length < k; i--)
    result.push(...buckets[i]);
  return result.slice(0, k);
}
Practice questions for this pattern
DSA Practice Online - 150+ Coding Interview Questions | LeetCode Alternative | InstaMock - AI Mock Interview