Introduction & Problem Explanation
If you are looking for a straightforward introduction to greedy algorithms, the Assign Cookies problem is a great place to start. The setup is simple: you have a group of children and a collection of cookies. Each child has a specific "greed factor"—the minimum cookie size they need to be happy. Each cookie also has a specific size. Your goal is to distribute the cookies to satisfy as many children as possible. However, there are two constraints: you can only give a child one cookie, and they won't accept any cookie smaller than their greed factor.
Let's look at a quick example:
- Children's greed factors:
g = [1, 2, 3] - Cookie sizes:
s = [1, 1]
1.
Think of this like trying to fit donated shoes onto a group of kids. If a child wears a size 6, they can comfortably wear a size 6 or larger, but a size 5 will be too tight. To make the most kids happy, you wouldn't just hand out shoes at random. A smart strategy is to line up the kids from smallest to largest feet, and lay out the shoes from smallest to largest size. You then take the child with the smallest feet and find the smallest available shoe that fits them. If a shoe is too small even for the kid with the smallest feet, no one else can wear it either—so you set it aside and move to the next shoe.
The Algorithmic Approach
Two-Pointer Greedy Search (O(n log n) Time, O(1) Space)
This is a classic candidate for a greedy algorithm. The core intuition is simple: to make the most children happy, we should avoid giving large cookies to kids with small appetites. Saving larger cookies for kids with higher greed factors gives us the best chance of satisfying them later. Here is the plan:
- Sort both groups: Sort the children's greed factors (
g) and the cookie sizes (s) in ascending order. - Track with two pointers: Start with two pointers at the beginning of each array (e.g.,
child = 0andcookie = 0). - Walk through the arrays: We loop while
child < g.lengthandcookie < s.length:- If the current cookie is big enough for the current child (
s[cookie] >= g[child]), we make a match. We move thechildpointer forward to focus on the next kid. - Whether the cookie was a match or too small, we always move the
cookiepointer forward. If a cookie is too small for the current child (who has the lowest greed among the remaining children), it won't satisfy any of the other children either.
- If the current cookie is big enough for the current child (
O(n log n + m log m) time, and the two-pointer loop runs in linear O(n + m) time.
Step-by-Step Execution Walkthrough
Let's trace this with a simple test case:
- Children greed:
g = [1, 2] - Cookie sizes:
s = [1, 2, 3]
0:
- Iteration 1: The cookie size is
1and the child's greed is1. Since1 >= 1, we have a match! We satisfy the first child, so both pointers move to index1. - Iteration 2: The cookie size is
2and the child's greed is2. Since2 >= 2, we have another match! We satisfy the second child. Both pointers increment again. - Termination: The
childpointer reaches the end of the array. We stop and return2, representing the number of satisfied children.
Key Code Snippets & Explanations
Let's look at the key details of the code logic:
Arrays.sort(g); Arrays.sort(s);: Sorting both inputs guarantees we are always matching the smallest requirements with the smallest resources first.if (s[cookie] >= g[child]) child++;: Checks if the cookie is large enough. If it is,child++marks that child as satisfied and ready for the next one.cookie++;: Ensures we move on to the next cookie in every step. Since we sorted the arrays, a cookie that fails to satisfy the current child is too small for anyone else and can be safely skipped.
Java Implementation Code
Here is the complete Java implementation of this solution. The main method includes a test run so you can see it in action.
package io.practise.dsa;
import java.util.Arrays;
public class AssignCookies {
// Sorting & Two Pointers: Time O(N log N + M log M), Space O(1)
public int findContentChildren(int[] g, int[] s) {
if (g == null || s == null) {
return 0;
}
// Sort children greed and cookie sizes
Arrays.sort(g);
Arrays.sort(s);
int child = 0;
int cookie = 0;
// Try matching the smallest greed with the smallest sufficient cookie
while (child < g.length && cookie < s.length) {
if (s[cookie] >= g[child]) {
child++; // Successfully satisfied this child, move to next child
}
cookie++; // Move to evaluate the next cookie
}
return child;
}
public static void main(String[] args) {
AssignCookies solver = new AssignCookies();
int[] g = {1, 2, 3}; // Greed factors of children
int[] s = {1, 1}; // Cookie sizes
System.out.println("--- Assign Cookies Demonstration ---");
System.out.println("Children Greed Factors: " + Arrays.toString(g));
System.out.println("Available Cookie Sizes: " + Arrays.toString(s));
int contentChildren = solver.findContentChildren(g, s);
System.out.println("Maximum satisfied children: " + contentChildren); // Expected: 1
}
}
Conclusion
The Assign Cookies problem demonstrates how a greedy strategy can yield an optimal global result by making optimal local decisions. By sorting the data, we align our resources with our requirements, satisfying the maximum number of children without wasting larger cookies.