Last Stone Weight
Problem Statement
You are given an array of integers stones
where stones[i]
represents the weight of the ith
stone.
We want to run a simulation on the stones as follows:
- At each step we choose the two heaviest stones, with weight
x
andy
and smash them togethers - If
x == y
, both stones are destroyed - If
x < y
, the stone of weightx
is destroyed, and the stone of weighty
has new weighty - x
.
Continue the simulation until there is no more than one stone remaining.
Return the weight of the last remaining stone or return 0
if none remain.
Example 1:
Input: stones = [2,3,6,2,4]
Output: 1
Explanation: We smash 6 and 4 and are left with a 2, so the array becomes [2,3,2,2]. We smash 3 and 2 and are left with a 1, so the array becomes [1,2,2]. We smash 2 and 2, so the array becomes [1].
Example 2:
Input: stones = [1,2]
Output: 1
Constraints:
1 <= stones.length <= 20
1 <= stones[i] <= 100
You should aim for a solution as good or better than O(n log n) time and O(n) space, where n is the size of the input array.
Recommended Time and Space Complexity
You should aim for a solution as good or better than O(n log n) time and O(n) space, where n is the size of the input array.
Hint 1
A naive solution would involve simulating the process by sorting the array at each step and processing the top 2
heaviest stones, resulting in an O(n * n log n) time complexity. Can you think of a better way? Consider using a data structure that efficiently supports insertion and removal of elements and maintain the sorted order.
Hint 2
We can use a Max-Heap, which allows us to retrieve the maximum element in O(1) time. We initially insert all the weights into the Max-Heap, which takes O(log n) time per insertion. We then simulate the process until only one or no element remains in the Max-Heap. At each step, we pop two elements from the Max-Heap which takes O(log n) time. If they are equal, we do not insert anything back into the heap and continue. Otherwise, we insert the difference of the two elements back into the heap.
Solution
Sorting
def last_stone_weight(stones: List[int]) -> int: while len(stones) > 1: stones.sort() cur = stones.pop() - stones.pop() if cur: stones.append(cur)
return stones[0] if stones else 0
Time complexity: O(n^2 log n) Space complexity: O(1)
Binary Search
def last_stone_weight(stones: List[int]) -> int: stones.sort() n = len(stones)
while n > 1: cur = stones.pop() - stones.pop() n -= 2 if cur > 0: l, r = 0, n while l < r: mid = (l + r) // 2 if stones[mid] < cur: l = mid + 1 else: r = mid pos = l n += 1 stones.append(0) for i in range(n - 1, pos, -1): stones[i] = stones[i - 1] stones[pos] = cur
return stones[0] if n > 0 else 0
Time complexity: O(n^2) Space complexity: O(1)
Heap
import heapq
def last_stone_weight(stones: List[int]) -> int: stones = [-s for s in stones] heapq.heapify(stones)
while len(stones) > 1: first = heapq.heappop(stones) second = heapq.heappop(stones) if second > first: heapq.heappush(stones, first - second)
return abs(stones[0]) if stones else 0
Time complexity: O(n log n) Space complexity: O(n)
Bucket Sort
def last_stone_weight(stones: List[int]) -> int: max_stone = max(stones) bucket = [0] * (max_stone + 1) for stone in stones: bucket[stone] += 1
first = second = max_stone while first > 0: if bucket[first] % 2 == 0: first -= 1 continue
j = min(first - 1, second) while j > 0 and bucket[j] == 0: j -= 1
if j == 0: return first second = j bucket[first] -= 1 bucket[second] -= 1 bucket[first - second] += 1 first = max(first - second, second) return first
Time complexity: O(n + w) Space complexity: O(w)