leetcode
leetcode 1901 ~ 1950
将数组和减半的最少操作次数

将数组和减半的最少操作次数

难度:

标签:

题目描述

代码结果

运行时间: 184 ms, 内存: 27.2 MB


/*
 * 题目思路:
 * 1. 使用流计算初始数组的总和。
 * 2. 用一个优先队列(最大堆)来存储所有数字,按从大到小排序。
 * 3. 每次从堆顶取出最大的数字,将其减半后再放回堆中,
 *    并更新当前数组和和减少的和,直到减少的和达到至少一半的初始和。
 * 4. 返回操作的次数。
 */
import java.util.PriorityQueue;
import java.util.stream.IntStream;

public class SolutionStream {
    public int minOperations(int[] nums) {
        double totalSum = IntStream.of(nums).asDoubleStream().sum();
        PriorityQueue<Double> maxHeap = new PriorityQueue<>((a, b) -> Double.compare(b, a));
        IntStream.of(nums).asDoubleStream().forEach(maxHeap::offer);
        double halfSum = totalSum / 2;
        double reducedSum = 0;
        int operations = 0;
        while (reducedSum < halfSum) {
            double maxVal = maxHeap.poll();
            double halfVal = maxVal / 2;
            reducedSum += halfVal;
            maxHeap.offer(halfVal);
            operations++;
        }
        return operations;
    }
}

解释

方法:

题解采用贪心策略,优先选择能对总和减少最大影响的数进行操作。首先计算数组的初始总和。数组进行排序后,从最大的数开始,每次选择当前最大的数,将其减半并累加操作次数,直到减半的累计值达到或超过原数组总和的一半。此外,使用一个双端队列(deque)来存储每次操作后生成的新数值,保证每次都能从当前可选的数中挑选最大的进行操作。

时间复杂度:

O(n log n)

空间复杂度:

O(n)

代码细节讲解

🦆
在使用双端队列(deque)存储减半数值时,如何保证队列中元素始终按照从大到小的顺序排列?
在题解中,每次操作后将新的数值加入双端队列时,新数值总是原数值的一半,因此通常比队列中已存在的元素要小。为保持队列的顺序,只需将新数值添加到队列的末端。此外,每次操作选择最大值时,比较队列首部和数组尾部的元素,这样可以保证从当前所有可选的数中选择最大的。
🦆
如果数组中包含多个相同的最大值,题解中的策略会如何选择进行操作,是否会影响最终的操作次数?
如果数组中包含多个相同的最大值,题解策略会从这些最大值中任选一个进行减半操作,因为它们都提供相同的减少总和的贡献。选择哪一个并不影响操作次数,因为每个数减半后的值相同,对总体目标的贡献一致。因此,即使选择不同的相同最大值进行操作,最终的操作次数也不会受到影响。
🦆
题解中提到`每次操作只将一个数减半`,为什么选择每次只操作一个数,而不是同时对多个数进行操作?
选择每次只将一个数减半是由于这样可以更精确地控制每次操作对总和的影响,保证每次操作都能最大限度地减少总和。同时操作多个数虽然看起来效率更高,但难以保证每次操作都是最优的,可能会导致需要更多的操作次数才能达到目标。此外,单个操作简化了问题的处理和队列的管理。
🦆
在题解的循环条件中,`while sub < total/2 and nums`表明循环直到减少的总和达到原总和的一半或者数组为空。数组为空但累计减少的总和未达到目标时,如何处理?
在这种情况下,实际上题解的设计意味着数组不可能在减少总和未达到目标时就变空。每次操作后,操作的数值会被减半后再次添加到队列中,所以队列和数组中总是有元素可用,直到减半的总和达到目标。因此,按照设计,总会有足够的数值可以操作,直到满足条件。

相关问题