LeetCopilot Logo
LeetCopilot
LeetCode Pattern/Greedy Algorithm/Greedy with Sorting: When to Sort, What to Sort By, and Complete Templates

Greedy with Sorting: When to Sort, What to Sort By, and Complete Templates

LeetCopilot Team
Dec 30, 2025
15 min read
Greedy AlgorithmSortingTwo PointersTemplatesInterview Prep
Master greedy problems that require sorting. Learn systematic decision frameworks for choosing sorting criteria, complete templates for two-pointer greedy, and solve Assign Cookies, Boats to Save People, and all variants.

Sorting is the secret weapon that unlocks greedy solutions. It transforms chaotic data into monotonic sequences where greedy decisions become obvious.

But here's the challenge: what do you sort by? Start time or end time? Ascending or descending? Single array or both? The wrong sorting criterion leads to wrong answers, and this is one of the most common greedy mistakes.

In this guide, you'll learn when sorting enables greedy, systematic frameworks for choosing sorting criteria, complete templates, and how to avoid the #1 sorting mistake.

TL;DR

Use greedy with sorting when:

  • Matching/pairing problems (assign cookies, boats)
  • Can create monotonicity through sorting
  • Need two-pointer greedy after sorting
  • Want O(n log n) time

Template:

python
arr1.sort()  # Sort by appropriate criterion
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 += 1, depends on problem

Key insight: Sorting creates monotonicity → enables greedy decisions.

Why Sorting Enables Greedy

Monotonicity and Greedy Decisions

Without sorting: No clear greedy choice

code
Children greed: [3, 1, 2]
Cookies: [1, 2, 1]

Which child gets which cookie? No obvious greedy strategy ✗

With sorting: Greedy choice becomes obvious

code
Children greed: [1, 2, 3] (sorted)
Cookies: [1, 1, 2] (sorted)

Greedy: Match smallest greed with smallest satisfying cookie
  Child 1 (greed=1) ← Cookie 1 (size=1) ✓
  Child 2 (greed=2) ← Cookie 3 (size=2) ✓
  Child 3 (greed=3) ← No cookies left

Result: 2 children satisfied (optimal) ✓

Why it works: Sorting creates monotonicity. If cookie j can't satisfy child i, it can't satisfy any child i+1, i+2, ... (who have higher greed). This enables greedy skipping.

When Sorting Helps vs Hurts

Sorting helps when:

  • ✅ Need to match/pair elements
  • ✅ Want to process in order (smallest to largest, etc.)
  • ✅ Can make greedy decisions based on sorted order
  • ✅ Don't need original indices

Sorting hurts when:

  • ❌ Need to preserve original order/indices
  • ❌ No monotonic property after sorting
  • ❌ Problem requires dynamic reordering
  • ❌ Sorting destroys required information

The Sorting Decision Framework

Question 1: What to Sort By?

Single array problems:

Problem TypeSort ByExample
Interval schedulingEnd timeMeeting Rooms
Greedy selectionValue, size, or ratioFractional Knapsack
Pairing with selfNatural orderTwo City Scheduling (by difference)

Two array problems:

Problem TypeSort Both ByExample
MatchingNatural orderAssign Cookies
PairingNatural orderBoats to Save People
Bipartite matchingNatural orderAdvantage Shuffle

Special cases:

Problem TypeSort ByExample
Sort by ratiovalue/weightFractional Knapsack
Sort by differencecost[A] - cost[B]Two City Scheduling
Sort by custom keyProblem-specificVarious

Question 2: Ascending or Descending?

Sort ascending when:

  • ✅ Want to process smallest first
  • ✅ Greedy choice is "take minimum"
  • ✅ Matching smallest with smallest

Sort descending when:

  • ✅ Want to process largest first
  • ✅ Greedy choice is "take maximum"
  • ✅ Matching largest with largest

Example:

python
# Assign Cookies: Sort both ascending
children.sort()  # Smallest greed first
cookies.sort()   # Smallest cookie first
# Match smallest greed with smallest satisfying cookie

# Fractional Knapsack: Sort descending by ratio
items.sort(key=lambda x: x.value/x.weight, reverse=True)
# Take highest value/weight ratio first

Question 3: Sort One or Both Arrays?

Sort both when:

  • ✅ Matching/pairing two arrays
  • ✅ Two-pointer greedy
  • ✅ Both need monotonicity

Sort one when:

  • ✅ One array is already sorted
  • ✅ Only one needs ordering
  • ✅ Other array is processed differently

Example:

python
# Assign Cookies: Sort BOTH
children.sort()
cookies.sort()

# Two Sum II: Array already sorted, don't sort again
# (given sorted array)

Template: Greedy with Two-Pointer After Sorting

Python Template

python
def greedy_two_pointer(arr1, arr2):
    """
    Template for greedy matching/pairing with two pointers.
    
    Args:
        arr1: First array (e.g., requirements)
        arr2: Second array (e.g., resources)
    
    Returns:
        Count of successful matches
    
    Time: O(n log n + m log m)
    Space: O(1)
    """
    # Sort both arrays (criterion depends on problem)
    arr1.sort()
    arr2.sort()
    
    # Two pointers
    i = j = 0
    count = 0
    
    while i < len(arr1) and j < len(arr2):
        # Check if current elements can be matched
        if can_match(arr1[i], arr2[j]):
            # Match successful
            count += 1
            i += 1
            j += 1
        else:
            # Can't match, move appropriate pointer
            # If arr2[j] too small, try next arr2
            # If arr2[j] too large, try next arr1
            j += 1  # Or i += 1, depends on problem
    
    return count

# Helper function (problem-specific)
def can_match(requirement, resource):
    return resource >= requirement

JavaScript Template

javascript
function greedyTwoPointer(arr1, arr2) {
    arr1.sort((a, b) => a - b);
    arr2.sort((a, b) => a - b);
    
    let i = 0, j = 0, count = 0;
    
    while (i < arr1.length && j < arr2.length) {
        if (canMatch(arr1[i], arr2[j])) {
            count++;
            i++;
            j++;
        } else {
            j++;
        }
    }
    
    return count;
}

function canMatch(requirement, resource) {
    return resource >= requirement;
}

Java Template

java
public int greedyTwoPointer(int[] arr1, int[] arr2) {
    Arrays.sort(arr1);
    Arrays.sort(arr2);
    
    int i = 0, j = 0, count = 0;
    
    while (i < arr1.length && j < arr2.length) {
        if (canMatch(arr1[i], arr2[j])) {
            count++;
            i++;
            j++;
        } else {
            j++;
        }
    }
    
    return count;
}

private boolean canMatch(int requirement, int resource) {
    return resource >= requirement;
}

Core Problems

1. Assign Cookies (LeetCode #455)

Problem: Maximize number of content children by assigning cookies.

Greedy insight: Match smallest greed with smallest satisfying cookie.

Solution:

python
def findContentChildren(g: List[int], s: List[int]) -> int:
    """
    Assign cookies to children to maximize satisfied children.
    
    Args:
        g: Children's greed factors
        s: Cookie sizes
    
    Returns:
        Maximum number of content children
    
    Time: O(n log n + m log m)
    Space: O(1)
    """
    # Sort both arrays ascending
    g.sort()
    s.sort()
    
    child = cookie = 0
    
    while child < len(g) and cookie < len(s):
        # If cookie satisfies child, move to next child
        if s[cookie] >= g[child]:
            child += 1
        # Always move to next cookie
        cookie += 1
    
    return child

# Example
print(findContentChildren([1,2,3], [1,1]))  # 1
# Child with greed 1 gets cookie size 1
# Children with greed 2,3 can't be satisfied

print(findContentChildren([1,2], [1,2,3]))  # 2
# Child 1 gets cookie 1, child 2 gets cookie 2

Why it works:

  • Sorting creates monotonicity
  • If cookie j can't satisfy child i, it can't satisfy any child > i
  • Greedy: Give smallest satisfying cookie to each child

Proof: If we give a larger cookie to a child with smaller greed, we waste resources. Matching smallest-to-smallest is optimal.

See detailed guide: Assign Cookies Greedy Explained

2. Boats to Save People (LeetCode #881)

Problem: Minimum boats needed (each boat holds max 2 people, weight limit).

Greedy insight: Pair heaviest with lightest to minimize boats.

Solution:

python
def numRescueBoats(people: List[int], limit: int) -> int:
    """
    Find minimum boats needed.
    
    Args:
        people: Array of people's weights
        limit: Maximum weight per boat
    
    Returns:
        Minimum number of boats
    
    Time: O(n log n)
    Space: O(1)
    """
    people.sort()
    
    left, right = 0, len(people) - 1
    boats = 0
    
    while left <= right:
        # Try to pair heaviest with lightest
        if people[left] + people[right] <= limit:
            left += 1  # Both can go in same boat
        # Heaviest always goes (alone or with lightest)
        right -= 1
        boats += 1
    
    return boats

# Example
print(numRescueBoats([1,2], 3))  # 1
# Both fit in one boat: 1+2=3 ≤ 3

print(numRescueBoats([3,2,2,1], 3))  # 3
# Boat 1: [1,2], Boat 2: [2], Boat 3: [3]

print(numRescueBoats([3,5,3,4], 5))  # 4
# Each person needs their own boat

Why it works:

  • Sort to enable greedy pairing
  • Heaviest person must go on a boat
  • Best case: Pair with lightest person
  • If can't pair, heaviest goes alone

Proof: If heaviest can't pair with lightest, they can't pair with anyone (everyone else is heavier than lightest). Greedy pairing is optimal.

See detailed guide: Greedy Two-Pointer Pattern

3. Two City Scheduling (LeetCode #1029)

Problem: Send n people to city A and n people to city B, minimize total cost.

Greedy insight: Sort by difference (costA - costB), send first half to A, second half to B.

Solution:

python
def twoCitySchedCost(costs: List[List[int]]) -> int:
    """
    Minimize cost of sending n people to each city.
    
    Args:
        costs: costs[i] = [costA, costB] for person i
    
    Returns:
        Minimum total cost
    
    Time: O(n log n)
    Space: O(1)
    """
    # Sort by difference: costA - costB
    # Negative difference = prefer city A
    # Positive difference = prefer city B
    costs.sort(key=lambda x: x[0] - x[1])
    
    n = len(costs) // 2
    total = 0
    
    # Send first n to city A (most prefer A)
    for i in range(n):
        total += costs[i][0]
    
    # Send last n to city B (most prefer B)
    for i in range(n, 2 * n):
        total += costs[i][1]
    
    return total

# Example
costs = [[10,20], [30,200], [400,50], [30,20]]
print(twoCitySchedCost(costs))  # 110

# Sorted by difference:
# [10,20] diff=-10 → send to A (cost 10)
# [30,20] diff=10  → send to B (cost 20)
# [30,200] diff=-170 → send to A (cost 30)
# [400,50] diff=350 → send to B (cost 50)
# Total: 10+30+20+50 = 110

Why it works:

  • Sort by "preference" (difference in costs)
  • People with negative difference prefer A
  • People with positive difference prefer B
  • Greedy: Send people to their preferred city

Proof: Sending someone to their less-preferred city increases cost by |difference|. Minimizing total difference minimizes total cost.

Ascending vs Descending

When to Sort Ascending

Use ascending when:

  • ✅ Process smallest first
  • ✅ Match smallest with smallest
  • ✅ Build up from minimum

Examples:

Assign Cookies:

python
g.sort()  # Ascending: smallest greed first
s.sort()  # Ascending: smallest cookie first
# Match smallest greed with smallest satisfying cookie

Boats to Save People:

python
people.sort()  # Ascending
# Pair smallest with largest

When to Sort Descending

Use descending when:

  • ✅ Process largest first
  • ✅ Greedy choice is "take maximum"
  • ✅ Want highest value first

Examples:

Fractional Knapsack:

python
items.sort(key=lambda x: x[1]/x[0], reverse=True)
# Descending by value/weight ratio
# Take highest ratio first

Maximum Units on Truck:

python
boxTypes.sort(key=lambda x: x[1], reverse=True)
# Descending by units per box
# Take boxes with most units first

When to Sort Both Arrays

Assign Cookies:

python
g.sort()  # Children's greed
s.sort()  # Cookie sizes
# Both ascending, match smallest-to-smallest

Advantage Shuffle:

python
A.sort()
B.sort()
# Both ascending, strategic matching

Common Mistakes

Mistake 1: Wrong Sorting Criterion

Wrong:

python
# Two City Scheduling
costs.sort(key=lambda x: x[0])  # WRONG: sort by costA only

Correct:

python
costs.sort(key=lambda x: x[0] - x[1])  # Sort by difference

Why it matters: Sorting by wrong key gives suboptimal solution.

See guide: Wrong Sorting Criterion

Mistake 2: Sorting When You Need Indices

Wrong:

python
# Problem asks for original indices
nums.sort()  # Loses original indices!

Correct:

python
# Track indices while sorting
indexed = [(val, i) for i, val in enumerate(nums)]
indexed.sort()

Mistake 3: Not Sorting Both Arrays

Wrong:

python
# Assign Cookies
g.sort()
# Forgot to sort s!

Correct:

python
g.sort()
s.sort()  # Sort both arrays

Mistake 4: Wrong Sort Direction

Wrong:

python
# Fractional Knapsack
items.sort(key=lambda x: x.value/x.weight)  # Ascending (wrong)

Correct:

python
items.sort(key=lambda x: x.value/x.weight, reverse=True)  # Descending

Advanced Techniques

Technique 1: Sort by Ratio

Problem: Fractional Knapsack

python
def fractionalKnapsack(values, weights, capacity):
    # Sort by value/weight ratio (descending)
    items = [(v/w, w, v) for v, w in zip(values, weights)]
    items.sort(reverse=True)
    
    total_value = 0
    
    for ratio, weight, value in items:
        if capacity >= weight:
            total_value += value
            capacity -= weight
        else:
            total_value += ratio * capacity
            break
    
    return total_value

Technique 2: Sort by Difference

Problem: Two City Scheduling

python
def twoCitySchedCost(costs):
    # Sort by difference (costA - costB)
    costs.sort(key=lambda x: x[0] - x[1])
    
    n = len(costs) // 2
    return sum(c[0] for c in costs[:n]) + sum(c[1] for c in costs[n:])

Technique 3: Sort with Custom Key

Problem: Reorder Data in Log Files

python
def reorderLogFiles(logs):
    def get_key(log):
        identifier, rest = log.split(' ', 1)
        if rest[0].isalpha():
            # Letter log: sort by content, then identifier
            return (0, rest, identifier)
        else:
            # Digit log: keep original order
            return (1,)
    
    return sorted(logs, key=get_key)

Complexity Analysis

Time Complexity:

  • Sorting: O(n log n) + O(m log m)
  • Two-pointer pass: O(n + m)
  • Total: O(n log n + m log m)

Space Complexity:

  • In-place sorting: O(1) or O(log n) for recursion
  • Total: O(1) (excluding sort overhead)

Comparison:

ApproachTimeSpaceWhen to Use
Greedy + SortO(n log n)O(1)Matching, pairing
Hash MapO(n)O(n)Need O(n) time
Brute ForceO(n²)O(1)Small inputs

Practice Problems

Master greedy with sorting:

Beginner:

  1. Assign Cookies (#455) - basic template
  2. Boats to Save People (#881) - two-pointer
  3. Maximum Units on Truck (#1710) - sort descending

Intermediate:
4. Two City Scheduling (#1029) - sort by difference
5. Advantage Shuffle (#870) - strategic matching
6. Minimum Deletions to Make Array Beautiful (#2216)

Advanced:
7. Minimum Cost to Hire K Workers (#857) - sort + heap
8. Maximize Sum of Array After K Negations (#1005)
9. Reduce Array Size to Half (#1338)

FAQ

Q: When should I sort ascending vs descending?

A: Sort ascending if you want smallest first (matching, building up). Sort descending if you want largest first (greedy selection of maximum).

Q: What if I need original indices?

A: Create pairs of (value, index) before sorting, or use a different approach (hash map).

Q: Should I always sort both arrays?

A: Only if both need monotonicity for greedy decisions. If one is already sorted or doesn't need sorting, don't sort it.

Q: How do I choose the sorting key?

A: Think about what makes an element "better" for greedy selection. Sort by that criterion.

Conclusion

Sorting is the key that unlocks greedy solutions for matching and pairing problems.

Key principles:

  • Sorting creates monotonicity → enables greedy decisions
  • Choose sorting criterion based on greedy choice
  • Sort ascending for smallest-first, descending for largest-first
  • Two-pointer greedy after sorting for matching

The template:

python
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

When to use:

  • Matching/pairing problems
  • Can create monotonicity
  • Want O(n log n) solution

Master this pattern, and you'll solve greedy sorting problems with confidence. For more patterns, see the complete greedy guide, Greedy vs DP, and Wrong Sorting Criterion.

Next time you see "assign" or "match optimally," reach for greedy with sorting.

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

Related Tutorials