leetcode
leetcode 2401 ~ 2450
收集元素的最少操作次数

收集元素的最少操作次数

难度:

标签:

题目描述

You are given an array nums of positive integers and an integer k.

In one operation, you can remove the last element of the array and add it to your collection.

Return the minimum number of operations needed to collect elements 1, 2, ..., k.

 

Example 1:

Input: nums = [3,1,5,4,2], k = 2
Output: 4
Explanation: After 4 operations, we collect elements 2, 4, 5, and 1, in this order. Our collection contains elements 1 and 2. Hence, the answer is 4.

Example 2:

Input: nums = [3,1,5,4,2], k = 5
Output: 5
Explanation: After 5 operations, we collect elements 2, 4, 5, 1, and 3, in this order. Our collection contains elements 1 through 5. Hence, the answer is 5.

Example 3:

Input: nums = [3,2,5,3,1], k = 3
Output: 4
Explanation: After 4 operations, we collect elements 1, 3, 5, and 2, in this order. Our collection contains elements 1 through 3. Hence, the answer is 4.

 

Constraints:

  • 1 <= nums.length <= 50
  • 1 <= nums[i] <= nums.length
  • 1 <= k <= nums.length
  • The input is generated such that you can collect elements 1, 2, ..., k.

代码结果

运行时间: 27 ms, 内存: 16.0 MB


/*
题目思路:
使用 Java Stream API 我们可以更简洁地实现目标。
我们依然从后往前遍历 nums,每次将元素添加到集合中。
使用 Stream API 的 allMatch 方法来检测集合中是否包含所有目标元素。
*/
import java.util.HashSet;
import java.util.Set;
import java.util.stream.IntStream;

public class Solution {
    public int minOperations(int[] nums, int k) {
        Set<Integer> set = new HashSet<>();
        int operations = 0;
        for (int i = nums.length - 1; i >= 0; i--) {
            set.add(nums[i]);
            operations++;
            if (IntStream.rangeClosed(1, k).allMatch(set::contains)) {
                return operations;
            }
        }
        return operations;
    }
}

解释

方法:

该题解的思路是从数组的末尾开始向前遍历,这是因为题目要求的是收集元素的最少操作次数,而操作仅允许从数组的末尾删除元素。为了统计收集到的元素,使用一个大小为 k+1 的布尔数组 vis 来标记哪些元素已被收集。数组的索引即元素的值。遍历数组的过程中,每遇到一个未收集的目标元素(即小于等于 k 的元素),就标记该元素为已收集,并更新已收集的元素计数器 cnt。一旦 cnt 达到 k,说明已收集到 1 到 k 的所有元素,此时的操作次数即为总长度减去当前索引,因为这表示从数组末尾到当前位置的元素数。

时间复杂度:

O(n)

空间复杂度:

O(k)

代码细节讲解

🦆
为什么选择从数组的末尾开始遍历而不是从开头或中间某处开始?
从数组末尾开始遍历是为了最小化必须删除的元素数量。题目中提到操作仅允许从数组的末尾删除元素,因此从末尾开始遍历可以直接计算出从当前位置到数组末端的元素数量,这些元素都需要被删除以达到收集目标元素的要求。如果从数组开头或中间开始,将难以直接得到需要删除的元素数,从而无法简单地计算最小操作次数。
🦆
在遍历过程中,如果遇到一个已经在集合中的元素,这种情况如何处理?是否会对结果产生影响?
在遍历过程中,如果遇到一个已经标记为收集的元素(即在布尔数组 vis 中已被设置为1的元素),我们将忽略这个元素继续遍历,因为它不会再次增加已收集元素的计数器 cnt。这种情况不会对结果产生影响,因为我们只关心是否收集了1到k的所有目标元素,而不是元素出现的次数。
🦆
布尔数组 vis 使用了 k+1 的大小,这里为什么要加1,直接使用 k 的大小不可以吗?
布尔数组 vis 使用 k+1 的大小是为了直接使用元素值作为数组的索引,方便快速检查和标记元素是否已被收集。如果使用 k 的大小,数组的索引将从0到k-1,这意味着我们无法直接用元素值 k 作为索引(因为索引k将超出数组范围)。因此,使用 k+1 的大小可以确保所有目标元素值都有一个对应的索引位置。
🦆
算法在确定已收集到所有目标元素后立即停止遍历,那么如果数组中还有未遍历的部分会怎样?
一旦确定已收集到所有目标元素,算法将立即停止遍历并返回当前操作的次数,因为此时已经达到了题目要求的最小操作次数的条件。数组中未遍历的部分不会再被考虑,因为它们不影响已达到的结果(即已经收集了1到k的所有目标元素),而继续遍历这部分只会增加不必要的计算,没有实际意义。

相关问题