The brute-force solution repeatedly searches for the maximum element, leading to:
The optimal solution uses a max heap to efficiently retrieve the heaviest stones, achieving O(n log n) time complexity:
In the “Last Stone Weight” problem, we are given a list of positive integers where each number represents the weight of a stone. The task is to simulate a process where we repeatedly select the two heaviest stones and smash them together. If the two stones have equal weights, they are both destroyed. If they differ, the stone with smaller weight is destroyed, and the heavier one is replaced with the difference of their weights. This continues until one or no stones remain. The goal is to return the weight of the last remaining stone or 0 if all stones are destroyed.
This problem resembles a kind of greedy strategy where, at every step, we aim to eliminate the largest weights first. The critical insight is that we must always compare the two heaviest stones, which suggests the use of a priority queue or max-heap to efficiently access these elements at each step.
Consider an example: [2, 7, 4, 1, 8, 1]
. The two heaviest stones are 8 and 7. Smashing them gives a new stone of weight 1. We then repeat this process with the updated set until one stone remains. Doing this efficiently is crucial for performance.
A heap allows us to repeatedly retrieve and remove the largest elements in logarithmic time. Since Python’s heapq
module implements a min-heap by default, we simulate a max-heap by storing the negative values of the stones. This way, the largest original values become the smallest in the heap structure.
We heapify the entire list initially in O(n) time, then repeatedly pop the top two elements (heaviest stones) and compute the result of smashing. If the result is non-zero, we push it back into the heap. This continues until the heap has one or no elements left.
The time complexity is O(n log n), where n is the number of stones. We heapify the list in O(n) time and then perform up to n log n operations for pushing and popping elements from the heap. The space complexity is O(n) due to the heap storage.
The input list might already contain just one stone—in that case, we return its weight immediately. If all stones cancel out through equal-weight collisions, we return 0. This solution is robust for any size input and performs well on larger datasets thanks to its use of heap operations rather than linear scans.
The “Last Stone Weight” problem is a great introduction to using heaps for greedy strategies involving frequent maximum extraction. By converting the problem to a max-heap simulation using negated values, we gain both performance and clarity. This approach highlights the power of data structures in optimizing repeated operations over dynamic collections.