Coding interviews are a weird beast. You’re sitting there, palms sweaty, trying to explain how to move people across a river while a senior dev watches your every typo. One problem that comes up constantly—seriously, it’s a favorite at places like Amazon and Google—is LeetCode 881, better known as Boats to Save People. It sounds simple on paper. You have a list of people with different weights and a limit on how much a boat can carry. Your job is to find the minimum number of boats to get everyone across. But here is the kicker: each boat can carry at most two people.
That two-person limit changes everything.
If you could stack an infinite number of people into a boat until you hit the weight limit, this would be a "Bin Packing Problem," which is famously NP-hard. It’s a nightmare. But because of that "two-person max" constraint, we can use a greedy strategy. It’s one of those rare moments where the simplest logic actually works perfectly.
The Core Logic of Boats to Save People
Most people approach this by trying to group the heaviest people together or the lightest people together. That’s a mistake. If you put two light people together, you’re basically wasting a boat’s capacity that could have been used to "offset" a heavy person.
Think about it this way. Imagine you have a guy who weighs 90kg and the boat limit is 100kg. That guy needs a boat. He’s taking up 90% of the capacity. If there is a person who weighs 10kg, you should absolutely put them with the 90kg guy. Why? Because the 90kg guy is going to take up a boat anyway. If you waste the 10kg person on someone lighter, you might end up needing an extra boat for the heavy guy later on.
To solve Boats to Save People efficiently, you have to sort the array first. Sorting is the "expensive" part of the algorithm, usually taking $O(n \log n)$ time. Once it's sorted, you use a two-pointer approach. You put one pointer at the very beginning (the skinniest person) and one at the very end (the heaviest person).
How the Two-Pointer Strategy Works in Practice
You look at the heaviest person and the lightest person together. Can they share a boat?
If their combined weight is less than or equal to the limit, great. You’ve saved a boat. You move both pointers inward.
But what if they’re too heavy?
The heavy person is the problem. They are so heavy they can’t even pair with the lightest person available. Since they have to get across, you give them their own boat. You move only the "heavy" pointer inward. The light person stays where they are, waiting for someone they can actually fit with.
It's efficient. It's clean. And it works every single time because the greedy choice—pairing the heaviest with the lightest—never prevents you from finding an optimal solution elsewhere.
Why Sorting is Non-Negotiable
You might wonder if you can do this without sorting. Honestly, not really. Without sorting, you have no way of knowing who the "lightest" and "heaviest" people are without scanning the list over and over again, which would tank your performance to $O(n^2)$. In a high-pressure interview, that’s a death sentence.
When you sort the weights, you’re organizing the data so the greedy choice becomes obvious. Let’s say the weights are [3, 2, 2, 1] and the limit is 3.
Sorted, they are [1, 2, 2, 3].
- Look at
1and3. Sum is 4. Too big. The3gets his own boat. (Total: 1 boat) - Look at
1and2. Sum is 3. Perfect fit. They share. (Total: 2 boats) - Only the middle
2is left. He gets his own boat. (Total: 3 boats)
Wait.
Actually, look at that again. If you had paired the 1 and 2 first, you’d still end up with the same result. The greedy property holds up because the most "restrictive" elements (the heavy people) are dealt with first.
Common Pitfalls and Edge Cases
The most common way people fail Boats to Save People isn't the logic; it's the implementation. They forget to handle the case where only one person is left. If your while loop isn't set up correctly (e.g., while left <= right vs while left < right), you might miss that last person standing on the dock.
🔗 Read more: Math Symbols Explained: Why We Use Weird Squiggles Instead of Words
Another subtle trap is the weight limit itself. Usually, the problem guarantees that no single person weighs more than the limit. But in a real-world scenario—or a particularly mean interview—you should check for that. If someone weighs 110kg and the boat limit is 100kg, you’re going to need a bigger boat, or just accept that they’re staying on the island.
Performance Constraints
When you're dealing with LeetCode, you have to look at the constraints. Usually, the number of people $n$ goes up to 50,000.
An $O(n^2)$ solution will time out.
An $O(n \log n)$ solution (sorting + two pointers) will pass with flying colors.
Memory-wise, you’re looking at $O(1)$ extra space if you sort in-place, or $O(n)$ if the language requires a copy. This is why recruiters love this question; it tests if you understand the trade-offs between time complexity and space complexity.
The Human Side of the Algorithm
It’s easy to get lost in the numbers, but this problem is a classic example of "Greedy Algorithms" in computer science. Greedy algorithms are like people who make the best possible decision at every single step without worrying about the future. Sometimes that leads to disaster (like the Traveling Salesman Problem). But for Boats to Save People, being greedy is actually the smartest thing you can do.
There’s a certain satisfaction in seeing the pointers meet in the middle. It’s like a puzzle clicking into place. You’re not just moving numbers; you’re optimizing resources.
Real-World Applications
While we don't often find ourselves literally sorting people by weight on a dock, the logic behind Boats to Save People applies to load balancing in servers and logistics in shipping. If you have containers of different sizes and fixed-capacity trucks, you’re using these exact principles. Efficient packing saves fuel, money, and time.
If you’re prepping for an interview, don’t just memorize the code. Understand why the heavy-light pairing works. If you can explain the intuition—that the heavy person is the bottleneck—you’ll impress the interviewer way more than if you just vomit out a perfect two-pointer loop.
Technical Implementation Details
If you're writing this in Python, the code is incredibly concise.
def numRescueBoats(people, limit):
people.sort()
left, right = 0, len(people) - 1
boats = 0
while left <= right:
if people[left] + people[right] <= limit:
left += 1
right -= 1
boats += 1
return boats
Notice how right -= 1 happens every time? That’s because the heavy person always gets a boat, whether they share it with a light person or go solo. The left += 1 only happens if they can fit a friend. It’s elegant.
Actionable Steps for Mastery
To truly master this problem and others like it, don't just stop at the solution.
Verify the Greedy Property: Try to come up with a counter-example where the "heaviest + lightest" strategy fails. You’ll find you can’t, as long as the limit is two people per boat.
Modify the Constraints: Ask yourself how you would solve this if three people could fit in a boat. Hint: it gets much, much harder (it becomes the 3-Partition problem).
Practice Manual Tracing: Take a random array like
[3, 5, 3, 4]with a limit of5. Sort it:[3, 3, 4, 5]. Walk through the pointers.3 + 5 > 5:5goes alone. (1 boat)3 + 4 > 5:4goes alone. (2 boats)3 + 3 > 5: Wait, no. Both3s are left. Wait,3 + 3 = 6. Each3goes alone. (4 boats total).- Actually, in that case, no one could share!
Time Yourself: You should be able to implement the two-pointer solution in under 5 minutes. If it takes longer, you're overthinking the loop conditions.
The beauty of Boats to Save People is that it rewards clear thinking over complex data structures. No trees, no graphs, no fancy hash maps—just a sorted list and a bit of common sense. Keep that in mind next time you’re staring at a blank whiteboard. Focus on the bottleneck, pair the extremes, and the solution usually reveals itself.