Кучи
Вес последнего камня
Каждый раз брать два самых тяжелых камня с помощью кучи
Постановка задачи
Описание (LeetCode - 1046. Last Stone Weight)
Дан массив stones, где stones[i] - вес i-го камня.
На каждом шаге мы выбираем два самых тяжелых камня x и y, где x <= y, и
сталкиваем их:
- если
x == y, оба камня уничтожаются; - если
x < y, камень весомxуничтожается, а камень весомyполучает весy - x.
Нужно вернуть вес последнего оставшегося камня. Если камней не осталось, вернуть
0.
Пример 1:
stones = [2, 7, 4, 1, 8, 1]
Результат:
1
Пояснение:
Сначала сталкиваем камни 7 и 8, получаем 1. Массив становится [2, 4, 1, 1, 1].
Затем сталкиваем 4 и 2, получаем 2. Массив становится [2, 1, 1, 1].
Дальше сталкиваем 2 и 1, получаем 1. Массив становится [1, 1, 1].
Потом сталкиваем 1 и 1, оба камня уничтожаются. Остается один камень весом
1 - это и есть ответ.
Пример 2:
stones = [1]
Результат:
1
Ограничения:
1 <= stones.length <= 301 <= stones[i] <= 1000
Решение
На каждом шаге нам нужны два самых тяжелых камня. Если каждый раз сортировать массив заново, решение будет работать, но делать много лишней работы. Мы постоянно меняем набор камней, поэтому лучше использовать структуру данных, которая быстро отдает максимум.
Идея решения:
Положим все камни в максимальную кучу. Пока в куче есть хотя бы два камня, достаем два самых тяжелых. Если их веса отличаются, кладем обратно разницу.
В конце возможны два случая:
- В куче остался один камень - возвращаем его вес.
- Куча пуста - возвращаем
0.
Основные шаги:
- Создаем максимальную кучу.
- Добавляем в нее все камни.
- Пока в куче больше одного камня:
- достаем самый тяжелый камень
y; - достаем второй самый тяжелый камень
x; - если
y != x, добавляемy - xобратно.
- достаем самый тяжелый камень
- Возвращаем оставшийся камень или
0.
Рассмотрим пример:
stones = [2, 7, 4, 1, 8, 1]
- Берем
8и7, остается1. - Берем
4и2, остается2. - Берем
2и1, остается1. - Берем
1и1, оба уничтожаются. - Остался камень весом
1.
Ответ: 1.
В языках, где есть только минимальная куча, можно хранить отрицательные веса. Тогда самый тяжелый камень превращается в самое маленькое отрицательное число.
Оценка сложности
Временная сложность
Построение кучи занимает O(n). На каждом шаге мы делаем извлечение и,
возможно, добавление за O(log n). Всего шагов не больше n.
Итоговая временная сложность: O(n log n).
Пространственная сложность
Куча хранит до n камней, поэтому дополнительная память равна O(n).
Код решения
Приведем код решения.
import (
"github.com/emirpasic/gods/trees/binaryheap"
"github.com/emirpasic/gods/utils"
)
func lastStoneWeight(stones []int) int {
inverseIntComparator := func(a, b interface{}) int {
return -utils.IntComparator(a, b)
}
heap := binaryheap.NewWith(inverseIntComparator)
for _, stone := range stones {
heap.Push(stone)
}
for heap.Size() > 1 {
yVal, _ := heap.Pop()
xVal, _ := heap.Pop()
y := yVal.(int)
x := xVal.(int)
if y != x {
heap.Push(y - x)
}
}
if heap.Empty() {
return 0
}
val, _ := heap.Pop()
return val.(int)
}Итоги
Куча подходит, потому что на каждом шаге нам нужны два текущих максимума, а набор камней постоянно меняется. Мы не поддерживаем полный отсортированный массив, а эффективно достаем только самые приоритетные элементы.