leetcode
leetcode 2751 ~ 2800
超过阈值的最少操作数 II

超过阈值的最少操作数 II

难度:

标签:

题目描述

You are given a 0-indexed integer array nums, and an integer k.

In one operation, you will:

  • Take the two smallest integers x and y in nums.
  • Remove x and y from nums.
  • Add min(x, y) * 2 + max(x, y) anywhere in the array.

Note that you can only apply the described operation if nums contains at least two elements.

Return the minimum number of operations needed so that all elements of the array are greater than or equal to k.

 

Example 1:

Input: nums = [2,11,10,1,3], k = 10
Output: 2
Explanation: In the first operation, we remove elements 1 and 2, then add 1 * 2 + 2 to nums. nums becomes equal to [4, 11, 10, 3].
In the second operation, we remove elements 3 and 4, then add 3 * 2 + 4 to nums. nums becomes equal to [10, 11, 10].
At this stage, all the elements of nums are greater than or equal to 10 so we can stop.
It can be shown that 2 is the minimum number of operations needed so that all elements of the array are greater than or equal to 10.

Example 2:

Input: nums = [1,1,2,4,9], k = 20
Output: 4
Explanation: After one operation, nums becomes equal to [2, 4, 9, 3].
After two operations, nums becomes equal to [7, 4, 9].
After three operations, nums becomes equal to [15, 9].
After four operations, nums becomes equal to [33].
At this stage, all the elements of nums are greater than 20 so we can stop.
It can be shown that 4 is the minimum number of operations needed so that all elements of the array are greater than or equal to 20.

 

Constraints:

  • 2 <= nums.length <= 2 * 105
  • 1 <= nums[i] <= 109
  • 1 <= k <= 109
  • The input is generated such that an answer always exists. That is, there exists some sequence of operations after which all elements of the array are greater than or equal to k.

代码结果

运行时间: 226 ms, 内存: 35.2 MB


/*
题目思路:
1. 使用Java Stream对数组进行排序。
2. 不断执行操作,选择最小的两个元素,将它们删除,并计算新元素加入数组。
3. 每次操作后重新插入新元素,并继续操作,直到数组中所有元素都大于或等于k。
4. 统计所需的操作次数。
*/
import java.util.Arrays;
import java.util.PriorityQueue;

public class Solution {
    public int minOperations(int[] nums, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<>(Arrays.stream(nums).boxed().toList());
        int operations = 0;
        while (pq.size() > 1 && pq.peek() < k) {
            int x = pq.poll();
            int y = pq.poll();
            int newElement = x * 2 + y;
            pq.offer(newElement);
            operations++;
        }
        return operations;
    }
}

解释

方法:

这个题解的核心思路是使用贪心算法结合两个双端队列(deque)来高效地处理数组。首先,对输入数组nums进行排序,然后使用两个队列q1和q2。q1初始化为排序后的nums,q2用于存放每次操作产生的新元素。在每次操作中,选择q1和q2队头的最小的两个元素进行合并操作,并将结果存入q2。这个过程重复进行,直到所有元素都大于等于k。操作过程中,如果在任意步骤发现已选的任一元素大于等于k,则可以提前结束操作。

时间复杂度:

O(n log n)

空间复杂度:

O(n)

代码细节讲解

🦆
在选择最小的两个元素进行合并时,为什么使用两个队列而不是一个队列或其他数据结构?
使用两个队列可以有效地区分原始数组元素和通过合并操作产生的新元素。q1队列包含排序后的原始数组元素,而q2队列则用来存放每次合并操作后生成的新元素。这种分离使得可以方便地从两个不同来源选择最小元素进行操作。如果只用一个队列,合并生成的新元素和原始元素会混合,使得每次操作时寻找最小的两个元素变得更复杂且效率更低。此外,使用其他数据结构如堆虽然可以维持元素的顺序,但在本问题中,使用两个队列可以更直观且操作简单地实现算法逻辑。
🦆
当从两个队列q1和q2中选择元素时,为什么优先选择队列q1的元素?
q1队列包含了初始排序后的数组元素,因此q1的元素在初始阶段是整个数据集中最小的。在选择两个最小元素进行合并以逐步增大元素值的过程中,优先选择q1的元素可以保证在早期的操作中使用尽可能小的元素,这有助于控制合成元素的增长速度,并尽可能地延迟较大元素的使用。这种策略有助于在提高每个元素值的同时,尽量减少总的操作次数。
🦆
如果在操作过程中q1已经为空,而q2包含了所有元素,这种情况下算法的效率如何?
当q1为空而q2包含所有元素时,算法的效率可能会略有下降。这是因为所有的操作必须使用q2中的元素,q2中的元素是之前合成生成的,可能会比q1中原始的最小元素大。尽管如此,由于q2中元素是按照合成顺序逐步增加的,所以这些元素还是相对较小并且合适合并的。在这种情况下,仍然可以进行有效的合并操作,只是整个数组的最小值合成路径变得单一,可能需要更多的合并步骤来达到所有元素都不小于k的目标。

相关问题