2Brute2Best
🔥0

Last Stone Weight

Easy·LeetCode #1046Basic Anchor
LeetCode
30-Second Mental ModelHeap — Kth element (max-heap of everything, take the top each turn)
Complexity:O(N² log N)→O(N log N)
Key Insight

A max-heap gives the heaviest stone in O(log N). Pop the two heaviest. If they differ, push back their difference. The pile is never sorted as a whole.

Core Validity Invariant

The top of the max-heap is always the heaviest remaining stone, so the two pops are always the two stones the rule asks for. The heap re-orders itself after every push and pop, so this stays true for the whole game.

01

Problem

You have a pile of stones. Stone i weighs stones[i].

Repeat this while two or more stones are left:

  • Take the two heaviest stones. Call the heavier one y and the lighter one x.
  • If x == y, both stones are destroyed.
  • If x != y, the lighter stone is destroyed and the heavier one now weighs y − x.

When one stone is left, return its weight. If no stones are left, return 0.

Input

stones, a list of stone weights.

Output

The weight of the last stone, or 0 if none is left.

Example 1
Input: stones = [2, 7, 4, 1, 8, 1]
Output: 1

7 and 8 → 1, so the pile is [2, 4, 1, 1, 1]. Then 4 and 2 → 2, pile [2, 1, 1, 1]. Then 2 and 1 → 1, pile [1, 1, 1]. Then 1 and 1 are equal, so both are destroyed. One stone (weight 1) is left.

Example 2
Input: stones = [1]
Output: 1

There is only one stone, so it is the last one.

Constraints
  • • 1 ≤ stones.length ≤ 30
  • • 1 ≤ stones[i] ≤ 1000
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    Each turn I need the two heaviest stones. Which data structure gives me the largest item quickly?

  2. 2.

    After a smash, the new stone can land anywhere in the weight order. Does that break a list I already sorted?

  3. 3.

    Each turn removes two stones and adds at most one. How many turns can happen at most?

  4. 4.

    What should the answer be when every stone is destroyed?

  5. 5.

    If I sort the pile once, what must I do after each smash to keep it correct?

03

Brute force approach

04

Better approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceSort the pile each turn and smash the two heaviest at the backO(N² log N)O(1)
BetterSort once; after each smash, insert the new stone at its sorted spotO(N²)O(1)
OptimalKeep the stones in a max-heap; pop the two heaviest, push the differenceO(N log N)O(N)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “the two heaviest stones”We need the current maximum, twice per turn. The maximum changes after every turn, so the structure must update fast.
  • “a new stone of weight y − x”The pile changes by one insert per turn. A heap takes one insert in O(log n) without re-sorting.
  • “until one stone is left”Keep looping on the size of the pile, not on a fixed count.
Repeatedly take the largest item+items change one at a time+max-heap of all items→Heap — Kth element (max-heap of everything, take the top each turn)

This is the anchor of the Kth element sub-pattern. The heap holds the whole pile, not just K items, because the pile keeps changing after each turn. The next questions keep only K items in a heap, which is where the size limit of K comes in.

10

Don't make this mistake

Min-heap instead of max-heap (pattern confusion)
✗ priority_queue<int, vector<int>, greater<int>> pile(stones.begin(), stones.end());
✓ priority_queue<int> pile(stones.begin(), stones.end());

greater<int> puts the lightest stone on top, so each turn smashes the two lightest stones. On the dry-run [2, 7, 4, 1, 8, 1] the min-heap pops 1 and 1 first (equal, both gone), then 2 and 4 (push 2), then 2 and 7 (push 5), then 5 and 8 (push 3). It returns 3. The correct answer is 1.

Reading top() of an empty heap
✗ return pile.top();
✓ return pile.empty() ? 0 : pile.top();

The input [2, 2] destroys both stones, so the heap is empty when the loop ends. top() on an empty priority_queue is undefined behaviour, usually a crash. The answer should be 0.

Keeping a sorted list but not inserting the new stone
✗ sort(...) once, then pop_back two stones and push_back(diff) at the end
✓ insert the new stone at its sorted spot, or use a heap

On the dry-run, after 8 and 7 are smashed, the pile is [1, 1, 2, 4, 1] because the new 1 was put at the end. The last two are then 1 and 4, but the real two heaviest are 4 and 2. The list is no longer sorted, so the wrong pair gets smashed.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like Heap — Top K translate into production architectures and low-level memory mechanics.