leetcode
leetcode 1651 ~ 1700
最高频元素的频数

最高频元素的频数

难度:

标签:

题目描述

代码结果

运行时间: 247 ms, 内存: 26.4 MB


/*
 * 思路:使用Java Stream无法完全处理整个问题,因为Stream不支持有状态的操作。
 * 但是可以使用Stream进行排序并收集数据,然后用传统方法处理剩下的部分。
 */
import java.util.Arrays;
import java.util.stream.IntStream;

public class MaxFrequencyStream {
    public int maxFrequency(int[] nums, int k) {
        nums = IntStream.of(nums).sorted().toArray();
        int left = 0, right = 0;
        int maxFreq = 1;
        long total = 0;

        while (right < nums.length) {
            total += nums[right];
            while ((right - left + 1) * nums[right] - total > k) {
                total -= nums[left];
                left++;
            }
            maxFreq = Math.max(maxFreq, right - left + 1);
            right++;
        }

        return maxFreq;
    }
}

解释

方法:

该题解使用了滑动窗口的思路。首先对数组进行排序,然后使用两个指针l和r表示窗口的左右边界。变量s存储为了使窗口内所有元素值相同所需的总增量。窗口扩展时,若s不超过k,右指针r向右移动;若s超过k,左指针l向右移动以缩小窗口,直到s不超过k。每次移动指针时,都会更新最大频率的可能值ans。这个方法依靠排序后的数组特性,保证窗口内的值可以通过增加操作转变为窗口右端的值。

时间复杂度:

O(n log n)

空间复杂度:

O(1)

代码细节讲解

🦆
题解中提到,为了使窗口内所有元素值相同所需的总增量s,能否具体解释一下如何计算s,以及它如何随窗口的扩展和缩小而变化?
在题解中,s代表为了使窗口内所有元素的值都等于窗口内最右端的元素值所需的总增量。具体计算方式是,每当右指针r向右移动以包含一个新的元素时,计算新增元素与窗口最右端之前的元素的差值,然后乘以窗口当前的宽度(即r-l),加到s上。这是因为,窗口内每个旧元素都需要增加这个差值才能与新的最右端元素对齐。当s超过允许的最大值k时,需要通过移动左指针l向右来缩小窗口,每向右移动一次,减去的值是窗口最左端的元素与新的最左端元素的差值,因为窗口缩小后,这部分增量不再需要。
🦆
在题解的代码中,为什么当s超过k时,仅通过移动左指针l来减少s,而不考虑其他可能的调整?
当s超过k时,表示窗口内的元素不能全部通过增加操作转变为窗口右端的值。在这种情况下,我们需要减少s以满足s<=k的条件。移动左指针l是最直接有效的方式来减少s,因为这会直接减少窗口的大小,相应地减少需要变更的元素数量。此外,移动右指针r以缩小窗口并不合适,因为这样做会损失掉窗口的扩展潜力,减少最大频率的可能性。
🦆
题解中的方法依赖于数组的排序。排序后,为什么可以保证通过增加操作将窗口内的值转变为窗口右端的值是最优的?
通过数组排序,我们可以确保窗口内的元素是有序的,这意味着窗口右端的值是窗口内最大的。因此,将所有窗口内的元素增加到窗口右端的值,总增量s会是最小的。如果选择将窗口内的元素变为其他非最右端的值,总增量s将会更大,因为窗口内更多的元素需要更大的增量来达到新的目标值。因此,排序后选择最右端的值作为目标是最优的策略,因为这样可以最小化所需的总增量s。

相关问题