LeetCode 1046. Last Stone Weight
题目描述
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出两块最重的石头,然后将它们一起粉碎。假设两块石头的重量分别为 x 和 y,且 x <= y:
- 如果
x == y,那么两块石头都会被完全粉碎; - 如果
x != y,那么重量为x的石头将会完全粉碎,而重量为y的石头新重量为y - x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。
例如:stones = [2,7,4,1,8,1]
- 第一轮:选出 8 和 7,剩下 8-7=1,数组变为
[2,4,1,1,1] - 第二轮:选出 4 和 2,剩下 4-2=2,数组变为
[2,1,1,1] - 第三轮:选出 2 和 1,剩下 2-1=1,数组变为
[1,1,1] - 第四轮:选出 1 和 1,完全粉碎,数组变为
[1] - 返回 1
解题思路
这道题完美适合使用最大堆(优先队列)来求解。因为每一轮都需要快速获取最大的两个元素,并在可能的情况下将差值插回。
大顶堆解法
- 将所有石头重量插入最大堆中。
- 当堆中元素数量 > 1 时,循环执行:
- 取出最大的元素
max1(堆顶)。 - 再取出次大的元素
max2。 - 如果
max1 != max2,将差值abs(max1 - max2)插回堆中。 - 如果
max1 == max2,两块石头都销毁,无需插回。
- 取出最大的元素
- 循环结束后:
- 如果堆为空,返回
0; - 否则返回堆中仅剩的一个元素。
- 如果堆为空,返回
注:代码中使用了自定义的
MyMaxHeap数据结构,其功能等价于java.util.PriorityQueue配合Collections.reverseOrder()(或者直接用负数存入小顶堆来模拟大顶堆)。
为什么用最大堆?
每一轮需要获取”最大的两个数”,最大堆可以在 O(log n) 时间内完成取出最大元素和插入新元素的操作。如果每次都对数组排序或扫描查找最大元素,时间复杂度会达到 O(n² log n) 或 O(n²),在大数据量下不可接受。
复杂度分析
- 时间复杂度:
O(n log n),其中n是石头数量。每轮操作(两次取出 + 一次插入)需要 O(log n),最多进行n-1轮。 - 空间复杂度:
O(n),最大堆需要存储所有石头重量。
代码
1 | package code.J1046; |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 𝒞𝒶𝓃𝒶𝓇𝓎!