Two Pointers | O(n²) → O(n) Optimization Technique Complete Guide

Two Pointers | O(n²) → O(n) Optimization Technique Complete Guide

이 글의 핵심

Practical guide to two pointers technique. Complete coverage of O(n²) → O(n) optimization with detailed examples.

Introduction

”Two Pointers: O(n²) → O(n)”

Two pointers is a technique that uses two indices to efficiently traverse an array.


1. Two Pointers Basics

Pattern 1: Start from Both Ends

def two_sum_sorted(arr, target):
    """
    Find two numbers in sorted array that sum to target
    """
    left, right = 0, len(arr) - 1
    
    while left < right:
        current_sum = arr[left] + arr[right]
        
        if current_sum == target:
            return [left, right]
        elif current_sum < target:
            left += 1  # Increase sum
        else:
            right -= 1  # Decrease sum
    
    return []

# Test
arr = [1, 2, 3, 4, 6]
print(two_sum_sorted(arr, 6))  # [1, 3] (2+4=6)

Why O(n)?

Naive approach (nested loops):
for i in range(n):
    for j in range(i+1, n):
        if arr[i] + arr[j] == target:
            return [i, j]
Time: O(n²)

Two pointers:
left, right = 0, n-1
while left < right:
    # Move one pointer per iteration
Time: O(n)

Pattern 2: Same Direction (Fast & Slow)

def remove_duplicates(arr):
    """
    Remove duplicates in sorted array (in-place)
    """
    if not arr:
        return 0
    
    write = 1  # Write pointer
    
    for read in range(1, len(arr)):  # Read pointer
        if arr[read] != arr[read - 1]:
            arr[write] = arr[read]
            write += 1
    
    return write

# Test
arr = [1, 1, 2, 2, 3, 4, 4]
length = remove_duplicates(arr)
print(arr[:length])  # [1, 2, 3, 4]

How It Works:

arr = [1, 1, 2, 2, 3, 4, 4]

Initial: write=1, read=1
Step 1: read=1, arr[1]=1 (duplicate, skip)
Step 2: read=2, arr[2]=2 (new, write), write=2
Step 3: read=3, arr[3]=2 (duplicate, skip)
Step 4: read=4, arr[4]=3 (new, write), write=3
Step 5: read=5, arr[5]=4 (new, write), write=4
Step 6: read=6, arr[6]=4 (duplicate, skip)

Result: [1, 2, 3, 4]

2. Practical Problems

Problem 1: Three Sum

def three_sum(arr):
    """
    Find three numbers that sum to 0
    [-1, 0, 1, 2, -1, -4] → [[-1, -1, 2], [-1, 0, 1]]
    """
    arr.sort()
    result = []
    
    for i in range(len(arr) - 2):
        # Skip duplicates
        if i > 0 and arr[i] == arr[i - 1]:
            continue
        
        left, right = i + 1, len(arr) - 1
        
        while left < right:
            total = arr[i] + arr[left] + arr[right]
            
            if total == 0:
                result.append([arr[i], arr[left], arr[right]])
                
                # Skip duplicates
                while left < right and arr[left] == arr[left + 1]:
                    left += 1
                while left < right and arr[right] == arr[right - 1]:
                    right -= 1
                
                left += 1
                right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1
    
    return result

# Test
arr = [-1, 0, 1, 2, -1, -4]
print(three_sum(arr))
# [[-1, -1, 2], [-1, 0, 1]]

Step-by-Step Trace:

arr = [-4, -1, -1, 0, 1, 2] (sorted)

i=0, arr[i]=-4:
  left=1(-1), right=5(2): -4-1+2=-3 (< 0, left++)
  left=2(-1), right=5(2): -4-1+2=-3 (< 0, left++)
  left=3(0), right=5(2): -4+0+2=-2 (< 0, left++)
  left=4(1), right=5(2): -4+1+2=-1 (< 0, left++)
  left=5, right=5: stop

i=1, arr[i]=-1:
  left=2(-1), right=5(2): -1-1+2=0 ✅ → [-1,-1,2]
  left=3(0), right=4(1): -1+0+1=0 ✅ → [-1,0,1]

i=2, arr[i]=-1: skip (duplicate)

Problem 2: Container With Most Water

def max_area(heights):
    """
    Maximum water between two lines
    """
    left, right = 0, len(heights) - 1
    max_water = 0
    
    while left < right:
        width = right - left
        height = min(heights[left], heights[right])
        water = width * height
        max_water = max(max_water, water)
        
        # Move shorter side
        if heights[left] < heights[right]:
            left += 1
        else:
            right -= 1
    
    return max_water

# Test
heights = [1, 8, 6, 2, 5, 4, 8, 3, 7]
print(max_area(heights))  # 49

Why Move Shorter Side?

heights = [1, 8, 6, 2, 5, 4, 8, 3, 7]
          L                       R

Current: width=8, height=min(1,7)=1, water=8*1=8

If move right (R--):
  width=7, height=min(1,3)=1, water=7*1=7 (worse)

If move left (L++):
  width=7, height=min(8,7)=7, water=7*7=49 (better!)

Lesson: Moving taller side never improves result

Problem 3: Subarray Sum

def subarray_sum(arr, target):
    """
    Count subarrays with sum equal to target (positive array)
    """
    left = 0
    current_sum = 0
    count = 0
    
    for right in range(len(arr)):
        current_sum += arr[right]
        
        # If sum > target, move left
        while current_sum > target and left <= right:
            current_sum -= arr[left]
            left += 1
        
        if current_sum == target:
            count += 1
    
    return count

# Test
arr = [1, 2, 3, 4, 5]
print(subarray_sum(arr, 5))  # 2 ([2,3], [5])

3. Two Pointers Patterns

Pattern Summary

# 1. Start from both ends (sorting required)
left, right = 0, len(arr) - 1
while left < right:
    # Move left++ or right-- based on condition

# 2. Same direction (fast & slow)
slow = 0
for fast in range(len(arr)):
    # Move slow++ when condition met

# 3. Range search
left = 0
for right in range(len(arr)):
    # Process range [left, right]
    while condition:
        left += 1

When to Use Each Pattern

PatternUse CaseExample
Both endsTwo sum, palindromeSorted array search
Same directionRemove duplicates, partitionIn-place modification
Range searchSubarray sum, substringVariable-length window

4. Advanced Techniques

Palindrome Check

def is_palindrome(s):
    """
    Check if string is palindrome (ignore non-alphanumeric)
    """
    left, right = 0, len(s) - 1
    
    while left < right:
        # Skip non-alphanumeric
        while left < right and not s[left].isalnum():
            left += 1
        while left < right and not s[right].isalnum():
            right -= 1
        
        if s[left].lower() != s[right].lower():
            return False
        
        left += 1
        right -= 1
    
    return True

# Test
print(is_palindrome("A man, a plan, a canal: Panama"))  # True

Partition Array

def partition(arr, pivot):
    """
    Partition array around pivot
    [3,1,4,2,5], pivot=3 → [1,2,3,4,5]
    """
    left = 0
    
    for right in range(len(arr)):
        if arr[right] < pivot:
            arr[left], arr[right] = arr[right], arr[left]
            left += 1
    
    return left

# Test
arr = [3, 1, 4, 2, 5]
pivot_idx = partition(arr, 3)
print(arr)  # [1, 2, 3, 4, 5]
print(pivot_idx)  # 2

Practical Tips

Coding Interview Tips

# ✅ Check if sorted
# Sort first if not sorted

# ✅ Handle duplicates
# Add logic to skip same values

# ✅ Boundary conditions
# Be careful with left < right vs left <= right

# ✅ Edge cases
# - Empty array
# - Single element
# - All same elements

Common Mistakes

# ❌ Wrong: Forgot to sort
arr = [3, 1, 4, 2, 5]
two_sum_sorted(arr, 6)  # Wrong result!

# ✅ Correct: Sort first
arr.sort()
two_sum_sorted(arr, 6)

# ❌ Wrong: Infinite loop
while left < right:
    if condition:
        # Forgot to move pointers!
        pass

# ✅ Correct: Always move pointers
while left < right:
    if condition:
        left += 1
    else:
        right -= 1

Summary

Key Points

  1. Two Pointers: Use two indices for O(n) traversal
  2. Patterns: Both ends, same direction, range search
  3. Optimization: O(n²) → O(n)
  4. Sorting: Usually used after sorting

Pattern Checklist

PatternMovementConditionExample
Both endsleft++, right—Compare sumTwo sum
Same directionslow++, fast++Compare valuesRemove duplicates
Range searchleft++, right++Window conditionSubarray sum

Baekjoon

LeetCode

  • LeetCode 1: Two Sum
  • LeetCode 15: 3Sum
  • LeetCode 11: Container With Most Water
  • LeetCode 26: Remove Duplicates from Sorted Array
  • LeetCode 125: Valid Palindrome

Programmers



Keywords

Two Pointers, Algorithm, Optimization, Two Sum, Three Sum, Subarray, Coding Interview, O(n), Sorted Array, Fast and Slow Pointers