从数量最多的堆取走礼物-LeetCode
2558. 从数量最多的堆取走礼物
🎯 问题描述
描述:
给你一个整数数组 gifts,表示各堆礼物的数量。每一秒,你需要执行以下操作:
- 选择礼物数量最多的那一堆(如果有多堆数量相同且最多,任选其一)。
- 将该堆的礼物数量减少到原来的平方根(向下取整)。
返回 k 秒后剩下的礼物总数量。
说明:
-
这是一个典型的**堆(优先队列)**应用问题。
-
核心策略:
- 题目要求每次都要找到当前“最大值”进行操作,这正是最大堆(大根堆)的拿手好戏。
- 由于 Python 的
heapq模块默认只提供最小堆,我们依然采用将数值取相反数(负数)的方法,来巧妙地模拟最大堆。 - 每次从堆顶取出绝对值最大的负数(即最多的礼物堆),计算其平方根后,再将结果的相反数放回堆中。
-
示例:
-
示例 1:
1 | 输入:gifts = [25,64,9,4,100], k = 4 |
💻 解题思路
思路1:最大堆(优先队列)模拟
核心逻辑
- 构建最大堆:
- 遍历
gifts数组,将所有礼物数量取相反数(-num),存入列表nums。 - 使用
heapq.heapify(nums)将列表原地转换为最小堆。此时堆顶(最小的负数)对应的就是原始数据中最大的礼物堆。
- 遍历
- 模拟 k 次取礼物操作:
- 循环
k次。每次从堆顶弹出最小的负数candidate。 - 将其还原为正数并计算平方根:
int((-candidate)**0.5)。 - 将计算结果的相反数(
-n)重新压入堆中,等待下一轮的比较。
- 循环
- 计算剩余总和:
- 操作结束后,堆中存储的是所有礼物堆最终数量的相反数。
- 对堆中所有元素求和并取相反数(
-sum(nums)),即可得到最终剩下的礼物总数。
代码实现
1 | import heapq |
📊 性能分析
- 时间复杂度:O((n + k) log n)
- 构建初始堆的时间复杂度为 O(n)。
- 每次
heappop和heappush操作的时间复杂度为 O(log n)。 - 我们一共进行
k次操作,因此总时间复杂度为 O((n + k) log n)。
- 空间复杂度:O(n)
- 需要一个额外的堆(列表)来存储 n 个礼物堆的数量。
思考
-
为什么要用负数模拟最大堆?
- 原理与“最后一块石头的重量”完全一致。Python 的
heapq只能弹出最小值。通过存入负数,原本最大的正数(如 100)变成了最小的负数(-100),正好位于堆顶,让我们每次都能 O(1) 地获取到当前最大的礼物堆。
- 原理与“最后一块石头的重量”完全一致。Python 的
-
关于平方根的计算:
- 代码中使用了
int((-candidate) ** 0.5)。** 0.5是计算平方根的常用方法,int()会自动向下取整,完美符合题目“减少到原来的平方根(向下取整)”的要求。
- 代码中使用了
-
最后求和的巧妙之处:
- 很多类似的题目最后只需要返回堆顶元素(如前几题)。但这道题要求返回剩余总和。
- 你的代码直接使用
-sum(nums),省去了逐个弹出堆元素的步骤,非常简洁高效。
-
数据范围的考量:
- 随着操作的进行,礼物数量会迅速减小(因为开平方根会让大数迅速变小)。这意味着即使
k很大,堆中的数值也会很快收敛到较小的值(比如 1 的平方根还是 1),程序的运行效率会非常高。
- 随着操作的进行,礼物数量会迅速减小(因为开平方根会让大数迅速变小)。这意味着即使
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 笺札!






