leetcode
leetcode 2701 ~ 2750
执行操作使频率分数最大

执行操作使频率分数最大

难度:

标签:

题目描述

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

You can perform the following operation on the array at most k times:

  • Choose any index i from the array and increase or decrease nums[i] by 1.

The score of the final array is the frequency of the most frequent element in the array.

Return the maximum score you can achieve.

The frequency of an element is the number of occurences of that element in the array.

 

Example 1:

Input: nums = [1,2,6,4], k = 3
Output: 3
Explanation: We can do the following operations on the array:
- Choose i = 0, and increase the value of nums[0] by 1. The resulting array is [2,2,6,4].
- Choose i = 3, and decrease the value of nums[3] by 1. The resulting array is [2,2,6,3].
- Choose i = 3, and decrease the value of nums[3] by 1. The resulting array is [2,2,6,2].
The element 2 is the most frequent in the final array so our score is 3.
It can be shown that we cannot achieve a better score.

Example 2:

Input: nums = [1,4,4,2,4], k = 0
Output: 3
Explanation: We cannot apply any operations so our score will be the frequency of the most frequent element in the original array, which is 3.

 

Constraints:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109
  • 0 <= k <= 1014

代码结果

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


/*
 * 思路:
 * 使用Java Streams对数组进行处理,实现与普通Java代码相同的逻辑。
 */

import java.util.Arrays;
import java.util.stream.IntStream;

public class SolutionStream {
    public int maxFrequency(int[] nums, long k) {
        int n = nums.length;
        Arrays.sort(nums);

        return IntStream.range(0, n)
                .map(right -> {
                    int left = 0;
                    long totalCost = 0;
                    for (int i = 1; i <= right; i++) {
                        totalCost += (long) (nums[i] - nums[i - 1]) * (i - left);
                        while (totalCost > k) {
                            totalCost -= nums[i] - nums[left];
                            left++;
                        }
                    }
                    return right - left + 1;
                })
                .max()
                .orElse(1);
    }
}

解释

方法:

该题解采用双指针的方法。首先对数组进行排序,然后使用两个指针i和j,其中i初始化为0,j从0开始遍历数组。在遍历的过程中,计算如果将区间[i, j]内的所有元素增加到nums[(i+j)//2]所需要的操作次数,并用k减去这个操作次数。如果k小于0,说明当前的区间不能通过k次操作达到所有元素相等,此时需要移动左指针i,并相应地调整k的值。最终,区间[i, j]的长度即为可以通过至多k次操作得到的最大频率分数。

时间复杂度:

O(n log n)

空间复杂度:

O(1)

代码细节讲解

🦆
在题解中提到了对数组进行排序,排序后使用中位数作为目标值能否保证是最优的选择?
在题解中,排序后使用中位数作为目标值是为了最小化总的操作次数,使所有元素变为同一个值。数学上,中位数是最小化所有数据点到一个共同点的距离总和的最佳选择。这是因为中位数将数据分为两个大小大致相等的部分,从而减少了向这个值调整的总距离。在这个问题的具体背景下,使用中位数来逼近可以确保用最少的操作次数将尽可能多的元素调整到这个值,从而最大化频率分数。
🦆
题解中的双指针方法在计算操作次数时,为什么选择`nums[(i+j)//2]`作为目标值?这样做的数学或逻辑依据是什么?
在使用双指针策略时,选择`nums[(i+j)//2]`作为目标值是出于减少总操作次数的考虑。当数组已排序时,中间的值(或中位数)是将所有值合并到一个点所需总调整距离最小的选择。因此,当计算区间[i, j]内所有元素变为相同值所需的最少操作次数时,选择中间的值作为目标可以确保在给定的操作次数k内,达到可能的最大区间长度。这种方法利用了排序数组中值的顺序和中位数减少总调整距离的特性,使得在限定操作次数内,可以尽可能扩大相同元素的频率。
🦆
当操作次数k变为负数时,题解选择移动左指针i并调整k的值,请问这种调整方式是否总是可行的?存在什么潜在的风险或限制?
当操作次数k变为负数时,意味着当前区间[i, j]不能仅通过k次操作使所有元素相等。这时移动左指针i是为了减少区间长度,尝试找到一个新的可行区间。通过调整k的值,即减去区间左端点的变化带来的操作数量变化,可以重新评估新的区间是否可行。这种调整方式通常是可行的,因为它通过缩小问题规模来寻找解决方案。然而,潜在的风险包括可能过早地缩小区间,尤其是在k的值本身就较小的情况下,这可能导致未能找到最优的最长区间。此外,如果所有元素差异较大,频繁地调整k和移动指针可能导致效率低下。

相关问题