The problem
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:
- If
x == y
, both stones are destroyed, and - If
x != y
, the stone of weightx
is destroyed, and the stone of weighty
has new weighty - 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
.
Examples
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.
Input: stones = [1]
Output: 1
Constraints
- 1 <= stones.length <= 30
- 1 <= stones[i] <= 1000
Explanation
From the description of the problem, we learn that we have a collection of stones that we can smash together at each turn. In case the stones have the same weight, both of the stones are going to be destroyed. If they are not the same weight, then the smaller stone will be destroyed, and the largest stone will be the difference between the weights.
For example, if we had stones with weights 7
and 6
, the stone with value 6
will be destroyed, and the larger stone’s weight will be reduced to 7 - 6 = 1
.
Sorting Solution
func lastStoneWeight(_ stones: [Int]) -> Int {
var stones = stones
while stones.count > 1 {
stones.sort()
let y = stones.removeLast()
let x = stones.removeLast()
let curr = y - x
if curr != 0 {
stones.append(curr)
}
}
if !stones.isEmpty {
return stones[0]
} else {
return 0
}
}
Explanation
One way to solve this problem is to sort our input each time before smashing stones.
It’s a valid solution but not very efficient because it will take O(n^2*log(n))
time.
We can solve this problem in a more efficient way by using a max heap data structure.
Time/ Space complexity
- Time complexity: O(n^2*log(n))
- Space complexity: O(1) or O(n) depending on the sorting algorithm
Max Heap Solution
func lastStoneWeight(_ stones: [Int]) -> Int {
var heap = Heap(stones)
while heap.count > 1 {
let y = heap.removeMax()
let x = heap.removeMax()
if x < y {
heap.insert(y - x)
}
}
if !heap.isEmpty {
return heap.max!
} else {
return 0
}
}
Explanation
Another way to solve this problem is to use a max heap data structure.
The first step that we will need to do is to create a max heap from our input, which will take O(n)
time.
Next, we will need to find the max
stones and smash them together.
Let’s look at our first example and imagine that we have already created a max heap.
- The first largest stone is going to be
8
, and the second will be equal to7
. We smash them together and get the result of8 - 7 = 1
, then we add1
to our array of stones. - The next biggest stones are
4
and2
. We smash them together and get4 - 2 = 2
, and add2
to our array of stones. - Now, we are left with values
2, 1, 1, 1
, so we smash stones with values2
and1
and get a new stone with value1
that we add to our array. - Lastly, we are left with three
1
’s, where two of them will be smashed together, and we will be left with a single stone with value1
that we will return as our result.
Time/ Space complexity
- Time complexity: O(n*log(n))
- Space complexity: O(n)