1046.最后一块石头的重量

59 阅读1分钟

题目:
有一堆石头,每块石头的重量都是正整数。

每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 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)
	}
}