题目:
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
- 如果
x == y,那么两块石头都会被完全粉碎; - 如果
x != y,那么重量为x的石头将会完全粉碎,而重量为y的石头新重量为y-x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。
算法:
方法一:大根堆
常规思路是每次排序,然后减少队列长度。
因为堆是大小有序的,我们每次都可以从当中很方便的取出最大值。
每次取出两个最大的石头粉碎,如果不相等则再放入一个石头。直到堆的容量<2
func lastStoneWeight(stones []int) int {
heap := NewHeap(stones)
for heap.Length() >= 2 {
stone1 := heap.Pop()
stone2 := heap.Pop()
if stone1 != stone2 {
heap.Push(stone1 - stone2)
}
}
if heap.Length() == 0 {
return 0
}
return heap.Pop()
}
type Heap struct {
heap []int
}
func NewHeap(nums []int) *Heap {
h := Heap{make([]int, 1)}
for i := range nums {
h.Push(nums[i])
}
return &h
}
func (h *Heap) Length() int {
// fmt.Println(h.heap)
return len(h.heap) - 1
}
func (h *Heap) Push(x int) {
h.heap = append(h.heap, x)
h.up(len(h.heap) - 1)
}
func (h *Heap) Pop() int {
n := len(h.heap)
x := h.heap[1]
h.heap[1], h.heap[n - 1] = h.heap[n - 1], h.heap[1]
h.heap = h.heap[:n - 1]
h.sink(1)
// fmt.Println("after pop", h.heap)
return x
}
func (h *Heap) sink(i int) {
child := 2 * i
// 先找到两个儿子中较大的,再和父亲比较,如果比父亲大,则交换位置
if child + 1 < len(h.heap) && h.heap[child + 1] > h.heap[child] {
child ++
}
if child < len(h.heap) && h.heap[i] < h.heap[child] {
h.heap[child], h.heap[i] = h.heap[i], h.heap[child]
h.sink(child)
}
}
func (h *Heap) up(i int) {
father := i / 2
if 0 < father && h.heap[father] < h.heap[i] {
h.heap[father], h.heap[i] = h.heap[i], h.heap[father]
h.up(father)
}
}