LeetCode ยท #1046

Last Stone Weight Solution

Smash the two heaviest stones together repeatedly. If they differ, the remainder stays. Return the weight of the last remaining stone, or 0 if none.

01
Section One ยท Problem

Problem Statement

๐Ÿท๏ธ

Difficulty

Easy

๐Ÿ”—

LeetCode

Problem #1046

๐Ÿ—๏ธ

Pattern

Heap โ€” simulation with max-heap

You have a collection of stones with positive integer weights. Each turn, pick the two heaviest stones and smash them. If they're equal, both are destroyed. If not, the lighter one is destroyed and the heavier one's weight is reduced by the lighter's weight. Return the weight of the last stone, or 0 if no stones remain.

Example
Input: stones = [2, 7, 4, 1, 8, 1] Output: 1 // Smash 8,7โ†’1. Smash 4,2โ†’2. Smash 2,1โ†’1. Smash 1,1โ†’0. Last stone: 1.
Constraints
โ€ข 1 โ‰ค stones.length โ‰ค 30 โ€ข 1 โ‰ค stones[i] โ‰ค 1000
02
Section Two ยท Approach 1

Brute Force โ€” Sort Every Turn O(nยฒ log n)

Sort the array each turn, take the last two elements (heaviest), smash them, and insert the remainder back. Repeat until one or zero stones remain.

Why it works

Sorting always places the two heaviest at the end. Correct by simulation โ€” we follow the problem's exact rules each round.

Why we can do better
Problem: Re-sorting an array of n elements after each smash costs O(n log n) per round, and there are up to nโˆ’1 rounds. A max-heap gives O(log n) per extraction and insertion โ€” overall O(n log n).
Java โ€” Sort each round
import java.util.*; class Solution { public int lastStoneWeight(int[] stones) { List<Integer> list = new ArrayList<>(); for (int s : stones) list.add(s); while (list.size() > 1) { Collections.sort(list); int a = list.remove(list.size() - 1); int b = list.remove(list.size() - 1); if (a != b) list.add(a - b); } return list.isEmpty() ? 0 : list.get(0); } }
Python โ€” Sort each round
class Solution: def lastStoneWeight(self, stones: list[int]) -> int: while len(stones) > 1: stones.sort() a, b = stones.pop(), stones.pop() if a != b: stones.append(a - b) return stones[0] if stones else 0
MetricValue
TimeO(nยฒ log n) โ€” sort O(n log n) per round ร— n rounds
SpaceO(n)
03
Section Three ยท Approach 2

Max-Heap โ€” O(n log n)

A max-heap always gives you the largest element in O(1) and extracts it in O(log n). Each round: pop the two largest, smash them, and push back the remainder if any. Repeat until โ‰ค 1 stone.

๐Ÿ’ก Mental model: Imagine a sumo tournament bracket. The two heaviest wrestlers always face each other in the final. The winner (difference in weight) drops down to fight in the next round. A max-heap is your tournament bracket โ€” the champion (heaviest) always floats to the top, and extracting the finalists costs O(log n) to re-arrange.
Algorithm
  • Step 1: Insert all stones into a max-heap.
  • Step 2: While heap has โ‰ฅ 2 elements: poll the two largest.
  • Step 3: If they differ, push first - second back.
  • Step 4: Return heap.peek() if one element remains, else 0.
๐ŸŽฏ When to recognize this pattern:
  • Any simulation that repeatedly needs the maximum (or minimum) element from a dynamic collection โ€” think heap.
  • The signal is "pick the best available, modify, put it back." This pattern appears in LC 1046 (Last Stone Weight), LC 621 (Task Scheduler), LC 767 (Reorganize String), and merge-k-sorted problems.
Java max-heap trick:
  • Java's PriorityQueue is a min-heap by default.
  • For a max-heap, use new PriorityQueue<>(Collections.reverseOrder()).
  • In Python, negate values: push -stone and pop gives the most-negative (i.e., largest original).
04
Section Four ยท Trace

Visual Walkthrough

Trace through stones = [2, 7, 4, 1, 8, 1]:

Max-Heap simulation โ€” smash two heaviest each round
INITIAL HEAP (max-heap) 8 7 4 2 1 1 Round 1: Poll 8, poll 7 โ†’ smash โ†’ 8-7 = 1 โ†’ push 1 Heap: [4, 2, 1, 1, 1] 5 stones remain Round 2: Poll 4, poll 2 โ†’ smash โ†’ 4-2 = 2 โ†’ push 2 Heap: [2, 1, 1, 1] 4 stones remain Round 3: Poll 2, poll 1 โ†’ smash โ†’ 2-1 = 1 โ†’ push 1 Heap: [1, 1, 1] 3 stones remain Round 4: Poll 1, poll 1 โ†’ equal โ†’ both destroyed (no push) Heap: [1] 1 stone remains โ†’ DONE LAST STONE: 1 Answer: 1 โœ“
05
Section Five ยท Implementation

Code โ€” Java & Python

Java โ€” Max-Heap (PriorityQueue reversed)
import java.util.*; class Solution { public int lastStoneWeight(int[] stones) { PriorityQueue<Integer> heap = new PriorityQueue<>(Collections.reverseOrder()); // max-heap for (int s : stones) heap.offer(s); while (heap.size() > 1) { int first = heap.poll(); // heaviest int second = heap.poll(); // second heaviest if (first != second) heap.offer(first - second); // remainder survives } return heap.isEmpty() ? 0 : heap.peek(); } }
Python โ€” Max-Heap (negate values)
import heapq class Solution: def lastStoneWeight(self, stones: list[int]) -> int: heap = [-s for s in stones] # negate for max-heap heapq.heapify(heap) while len(heap) > 1: first = -heapq.heappop(heap) # largest (un-negate) second = -heapq.heappop(heap) if first != second: heapq.heappush(heap, -(first - second)) return -heap[0] if heap else 0
06
Section Six ยท Analysis

Complexity Analysis

ApproachTimeSpaceTrade-off
Sort every roundO(nยฒ log n)O(n)Simple but redundant re-sorting
Sorted insert (bisect)O(nยฒ)O(n)O(log n) find + O(n) shift per round
Max-Heap โ† optimal O(n log n) O(n) O(log n) per poll/offer ร— n rounds
Why not a sorted array?:
  • Inserting into a sorted array is O(n) due to shifting elements. A heap gives O(log n) per insertion and extraction.
  • For n=30 the difference is negligible, but the heap solution is the standard pattern for interviews.
07
Section Seven ยท Edge Cases

Edge Cases & Pitfalls

CaseInputWhy It Matters
Single stone[5]No smashing needed โ†’ return 5.
Two equal stones[3, 3]Both destroyed โ†’ heap empty โ†’ return 0.
All same weight[2, 2, 2, 2]Pair-wise destruction โ†’ return 0 (even count) or one stone (odd).
Already sorted ascending[1, 2, 3]Heap reorders to [3, 2, 1]. Smash 3-2=1, then 1-1=0 โ†’ return 0.
Large difference[1000, 1]999 survives. One round, one stone remains.
All ones[1, 1, 1]Smash 1,1โ†’0. One stone (1) remains.
โš  Common Mistake (Python): Forgetting to negate when pushing the remainder back. After computing first - second, you must push -(first - second) to maintain the max-heap invariant. Pushing the positive difference treats it as a small number in the min-heap, corrupting the order.

โ† Back to Heap problems