You are given an array of integers stones
where stones[i]
is the weight of the ith
stone.
We are playing a game with the stones. On each turn, we choose the heaviest two stones and smash them together. Suppose the heaviest two stones have weights x
and y
with x <= y
. The result of this smash is:
x == y
, both stones are destroyed, andx != y
, the stone of weight x
is destroyed, and the stone of weight y
has new weight y - x
.At the end of the game, there is at most one stone left.
Return the weight of the last remaining stone. If there are no stones left, return 0
.
Example 1:
Input: stones = [2,7,4,1,8,1] Output: 1 Explanation: We combine 7 and 8 to get 1 so the array converts to [2,4,1,1,1] then, we combine 2 and 4 to get 2 so the array converts to [2,1,1,1] then, we combine 2 and 1 to get 1 so the array converts to [1,1,1] then, we combine 1 and 1 to get 0 so the array converts to [1] then that's the value of the last stone.
Example 2:
Input: stones = [1] Output: 1
Constraints:
1 <= stones.length <= 30
1 <= stones[i] <= 1000
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for the last stone weight problem involves simulating every possible combination of stone crushing until only one stone (or none) remains. We try every pair of stones, find the difference, and then try all pairs again with the updated collection of stones. This continues until we are left with zero or one stones.
Here's how the algorithm would work step-by-step:
def last_stone_weight_brute_force(stones):
while len(stones) > 1:
heaviest_stone_index = 0
second_heaviest_stone_index = 1
# Find the two heaviest stones
if stones[second_heaviest_stone_index] > stones[heaviest_stone_index]:
heaviest_stone_index, second_heaviest_stone_index = second_heaviest_stone_index, heaviest_stone_index
for index in range(2, len(stones)):
if stones[index] > stones[heaviest_stone_index]:
second_heaviest_stone_index = heaviest_stone_index
heaviest_stone_index = index
elif stones[index] > stones[second_heaviest_stone_index]:
second_heaviest_stone_index = index
# Simulate crushing the stones.
if stones[heaviest_stone_index] == stones[second_heaviest_stone_index]:
#Remove both stones since weights are equal
if heaviest_stone_index > second_heaviest_stone_index:
stones.pop(heaviest_stone_index)
stones.pop(second_heaviest_stone_index)
else:
stones.pop(second_heaviest_stone_index)
stones.pop(heaviest_stone_index)
else:
#Keep the difference after crushing
stones[heaviest_stone_index] = stones[heaviest_stone_index] - stones[second_heaviest_stone_index]
stones.pop(second_heaviest_stone_index)
if len(stones) == 0:
return 0
else:
return stones[0]
The problem asks us to simulate smashing stones together until only one (or none) remains. The most efficient approach is to always smash the two heaviest stones together at each step. This way, we quickly reduce the number of stones and focus on the most impactful operations.
Here's how the algorithm would work step-by-step:
import heapq
def lastStoneWeight(stones):
stones = [-stone for stone in stones]
heapq.heapify(stones)
while len(stones) > 1:
# Get the two heaviest stones
stone_one = heapq.heappop(stones)
stone_two = heapq.heappop(stones)
# If the stones are not equal, calculate the difference
if stone_one != stone_two:
# Push the difference back into the heap
heapq.heappush(stones, stone_one - stone_two)
# If there is a stone remaining return its weight
if stones:
return -stones[0]
else:
return 0
Case | How to Handle |
---|---|
Null or empty input array | Return 0 immediately as there are no stones to smash. |
Input array with a single stone | Return the weight of the single stone directly. |
Input array with two stones of equal weight | The stones are destroyed, resulting in 0, so the algorithm should correctly handle this scenario and return 0. |
Input array with two stones of different weights | The algorithm should compute the difference and return the result. |
Input array with all identical stone weights | The algorithm should repeatedly smash pairs of equal weights until only one or zero stones remain, handling repeated subtractions correctly. |
Large input array with widely varying stone weights | Using a max-heap data structure ensures the two heaviest stones are efficiently selected, allowing the solution to scale well. |
Extremely large stone weights potentially leading to integer overflow | Use a data type with a larger range (e.g., long) to prevent integer overflow during subtractions. |
A sequence of operations resulting in an empty heap or single remaining element | The algorithm needs to correctly iterate until the heap has at most one element, returning 0 for an empty heap or the single element's weight. |