Problem: 2558. 从数量最多的堆取走礼物
每日一题。
思路
找最大,对最大值求平方根+求和。下意识在原数组直接改动,后联系大顶堆(任意结点>=其子节点)
Code
图一乐(排序操作)
从小到大排序,对末尾下标操作。
public long pickGifts(int[] gifts, int k) {
long sum = 0;
for (int i = gifts.length - 1; i >= gifts.length - k; i--) {
Arrays.sort(gifts);// 排序
gifts[gifts.length - 1] = (int) Math.floor(Math.sqrt(gifts[gifts.length - 1]));// 下标操作
}
for (int i : gifts) {
sum += (long) i;
}
return sum;
}
大顶堆
class Solution {
public long pickGifts(int[] gifts, int k) {
long sum = 0;
// 实例化堆并写入
Queue<Integer> queue = new PriorityQueue<>((a,b)->(b-a));// 默认小顶堆
for (int i : gifts) {
queue.offer(i);
}
for (int i = 0; i < k; i++) {
int max = (int) queue.poll();// 获取堆顶
int sqrt = (int) Math.floor(Math.sqrt(max));
queue.offer(sqrt);
}
for (Integer i : queue) {
sum += i;
}
return sum;
}
}
原地堆化(灵神的代码)
原地址
添加了部分注释。
public long pickGifts(int[] gifts, int k) {
heapify(gifts); // 原地堆化(大顶堆)
// 判断堆顶是否为1,节省运行时间,不判断会进入下沉方法中退出。
while (k-- > 0 && gifts[0] > 1) {
gifts[0] = (int) Math.sqrt(gifts[0]); // 直接修改堆顶
// 下沉方法
sink(gifts, 0); // 堆化(只需要把 gifts[0] 下沉)
}
long ans = 0;
for (int x : gifts) {
ans += x;
}
return ans;
}
// 原地堆化(大顶堆)
// 堆化可以保证 h[0] 是堆顶元素,且 h[i] >= max(h[2*i+1], h[2*i+2])
private void heapify(int[] h) {
// h.length / 2 - 1 倒着遍历,保证堆结构
for (int i = h.length / 2 - 1; i >= 0; i--) {
sink(h, i);
}
}
// 下沉方法
private void sink(int[] h, int i) {
int n = h.length;
while (2 * i + 1 < n) {
int j = 2 * i + 1; // i 的左儿子
if (j + 1 < n && h[j + 1] > h[j]) { // i 的右儿子比 i 的左儿子大
j++;
}
if (h[j] <= h[i]) { // 说明 i 的左右儿子都 <= h[i],停止下沉
break;
}
swap(h, i, j); // 下沉
i = j;
}
}
// 交换 h[i] 和 h[j]
private void swap(int[] h, int i, int j) {
int tmp = h[i];
h[i] = h[j];
h[j] = tmp;
}