The greedy two-pointer pattern combines two powerful techniques: greedy sorting + two-pointer matching.
This hybrid approach solves a specific class of problems where you need to pair or match elements optimally.
TL;DR
Pattern: Sort both arrays, use two pointers to greedily match elements.
When to use: Matching, pairing, assignment problems with constraints.
Template:
arr1.sort()
arr2.sort()
i = j = 0
while i < len(arr1) and j < len(arr2):
if can_match(arr1[i], arr2[j]):
i += 1
j += 1
else:
j += 1 # Or i += 1Time: O(n log n), Space: O(1)
The Core Idea
Why combine greedy + two pointers?
- Greedy: Sorting establishes optimal order
- Two pointers: Efficiently find matches in sorted arrays
Key insight: After sorting, you can make greedy decisions by comparing elements at pointer positions.
Pattern Variants
Variant 1: Same Direction (Both Increasing)
Use when: Matching elements from two arrays.
def match_elements(arr1, arr2):
arr1.sort()
arr2.sort()
i = j = 0
matches = 0
while i < len(arr1) and j < len(arr2):
if arr2[j] >= arr1[i]: # Can match
matches += 1
i += 1
j += 1
else:
j += 1 # Try next element in arr2
return matchesExample: Assign Cookies (LC #455)
Variant 2: Opposite Direction (Left + Right)
Use when: Pairing elements from same array.
def pair_elements(arr, limit):
arr.sort()
left = 0
right = len(arr) - 1
pairs = 0
while left < right:
if arr[left] + arr[right] <= limit:
pairs += 1
left += 1
right -= 1
else:
right -= 1 # Try smaller element
return pairsExample: Boats to Save People (LC #881)
Example 1: Boats to Save People
Problem: Each boat holds at most 2 people and has weight limit. Find minimum boats needed.
Greedy insight: Pair heaviest with lightest if possible.
def numRescueBoats(people, limit):
"""
Time: O(n log n), Space: O(1)
"""
people.sort()
left = 0
right = len(people) - 1
boats = 0
while left <= right:
# Try to pair heaviest with lightest
if people[left] + people[right] <= limit:
left += 1 # Lightest person gets on
right -= 1 # Heaviest person gets on
else:
right -= 1 # Heaviest goes alone
boats += 1
return boatsWhy it works:
- Heaviest person must go on a boat
- If they can pair with lightest, that's optimal
- If not, they go alone (no better option)
Trace:
people = [3, 2, 2, 1], limit = 3
After sort: [1, 2, 2, 3]
Step 1: left=0, right=3
people[0]=1, people[3]=3
1 + 3 = 4 > 3? YES
Heaviest (3) goes alone
boats=1, right=2
Step 2: left=0, right=2
people[0]=1, people[2]=2
1 + 2 = 3 <= 3? YES
Pair them
boats=2, left=1, right=1
Step 3: left=1, right=1
Same person, goes alone
boats=3
Result: 3 boatsExample 2: Two City Scheduling
Problem: 2n people, costs to send to city A or B. Send n to each city, minimize total cost.
Greedy insight: Sort by difference (cost_A - cost_B), send first n to A, rest to B.
def twoCitySchedCost(costs):
"""
Time: O(n log n), Space: O(1)
"""
# Sort by difference: how much we save by going to A
costs.sort(key=lambda x: x[0] - x[1])
total = 0
n = len(costs) // 2
# First n go to city A (biggest savings)
for i in range(n):
total += costs[i][0]
# Last n go to city B
for i in range(n, 2 * n):
total += costs[i][1]
return totalWhy it works:
- Sorting by difference identifies who benefits most from city A
- Greedy: send those with biggest A-savings to A
- Two-pointer-like: split sorted array in half
Example 3: Advantage Shuffle
Problem: Rearrange arr1 to maximize number of positions where arr1[i] > arr2[i].
Greedy insight: Match smallest arr1 that beats arr2[i], or use smallest arr1 if can't beat.
from collections import deque
def advantageCount(nums1, nums2):
"""
Time: O(n log n), Space: O(n)
"""
nums1.sort()
# Create (value, index) pairs and sort by value
sorted_nums2 = sorted((val, i) for i, val in enumerate(nums2))
result = [0] * len(nums1)
left = 0
right = len(nums1) - 1
# Process nums2 from largest to smallest
for val, idx in reversed(sorted_nums2):
if nums1[right] > val:
# Can beat it, use largest available
result[idx] = nums1[right]
right -= 1
else:
# Can't beat it, use smallest (waste it)
result[idx] = nums1[left]
left += 1
return resultWhy it works:
- For largest nums2 values, try to beat with largest nums1
- If can't beat, waste smallest nums1 (save larger for later)
- Two pointers track available nums1 elements
The Universal Template
def greedy_two_pointer(arr1, arr2, condition):
"""
Universal template for greedy + two pointer.
"""
# Step 1: Sort (greedy ordering)
arr1.sort()
arr2.sort() # If needed
# Step 2: Initialize pointers
left = 0
right = len(arr1) - 1 # Or use two arrays
result = 0
# Step 3: Two-pointer logic
while left <= right: # Or other condition
if condition(arr1[left], arr1[right]):
# Make greedy choice
result += 1
left += 1
right -= 1
else:
# Adjust pointers
right -= 1 # Or left += 1
return resultWhen to Use This Pattern
Use greedy + two pointers when:
- ✅ Need to match/pair elements
- ✅ Optimal pairing depends on relative ordering
- ✅ Can make greedy decision after sorting
- ✅ Each element used at most once
Don't use when:
- ❌ Order doesn't matter
- ❌ Need to consider all pairs (use DP)
- ❌ Elements can be reused
Common Mistakes
Mistake 1: Wrong Sort Direction
❌ Wrong:
arr.sort(reverse=True) # Descending when need ascending✅ Correct:
arr.sort() # Ascending for greedy matchingMistake 2: Moving Both Pointers Always
❌ Wrong:
left += 1
right -= 1 # Always move both✅ Correct:
if condition:
left += 1
right -= 1
else:
right -= 1 # Only move oneMistake 3: Not Sorting First
❌ Wrong:
# Forgot to sort!
left, right = 0, len(arr) - 1✅ Correct:
arr.sort() # Must sort first
left, right = 0, len(arr) - 1Complexity Analysis
Time Complexity:
- Sort: O(n log n)
- Two-pointer scan: O(n)
- Total: O(n log n)
Space Complexity:
- Sorting: O(1) if in-place
- Pointers: O(1)
- Total: O(1)
Related Problems
Practice these:
- Assign Cookies (LC #455) - Same direction matching
- Boats to Save People (LC #881) - Opposite direction pairing
- Two City Scheduling (LC #1029) - Sort by difference
- Advantage Shuffle (LC #870) - Strategic matching
- Minimum Number of Arrows (LC #452) - Interval merging variant
Decision Tree: Which Variant?
Need to match elements?
├─ From two arrays?
│ └─ Use same-direction (i, j)
│ Example: Assign Cookies
│
└─ From same array?
└─ Use opposite-direction (left, right)
Example: Boats to Save PeopleConclusion
The greedy + two-pointer pattern is powerful for matching and pairing problems:
Key steps:
- Sort to establish greedy order
- Initialize pointers (same or opposite direction)
- Match greedily based on sorted order
- Move pointers based on match result
Remember: Sorting enables greedy decisions, two pointers enable efficient matching.
For more patterns, see Greedy with Sorting Template and the complete greedy guide.
Want to Practice LeetCode Smarter?
LeetCopilot is a free browser extension that enhances your LeetCode practice with AI-powered hints, personalized study notes, and realistic mock interviews — all designed to accelerate your coding interview preparation.
Also compatible with Edge, Brave, and Opera
