【C/C++】1224. 最大相等频率

372 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第28天,点击查看活动详情


题目链接:1224. 最大相等频率

题目描述

给你一个正整数数组 nums,请你帮忙从该数组中找出能满足下面要求的 最长 前缀,并返回该前缀的长度:

  • 从前缀中 恰好删除一个 元素后,剩下每个数字的出现次数都相同。

如果删除这个元素后没有剩余元素存在,仍可认为每个数字都具有相同的出现次数(也就是 0 次)。

提示:

  • 2nums.length1052 \leqslant nums.length \leqslant 10^5
  • 1nums[i]1051 \leqslant nums[i] \leqslant 10^5

示例 1:

输入:nums = [2,2,1,1,5,3,3,5]
输出:7
解释:对于长度为 7 的子数组 [2,2,1,1,5,3,3],如果我们从中删去 nums[4] = 5,就可以得到 [2,2,1,1,3,3],里面每个数字都出现了两次。

示例 2:

输入: nums = [1,1,1,2,2,2,3,3,3,4,4,4,5]
输出: 13

整理题意

题目给定一个整数数组,让我们找到数组中 最长 的前缀,要求这个前缀满足:从前缀中 恰好删除一个 元素后,剩下每个数字的出现次数都相同。

题目规定答案最小值为 1,因为删除后数组没有元素也可以认为所有元素出现次数相同,且为 0

解题思路分析

首先我们考虑需要记录的数是什么:

  1. 统计每个数出现的次数,因为题目求的是前缀删除一个元素后剩余元素出现次数相同。
  2. 统计出现次数的频数,这是因为我们可以通过频数乘以次数得到元素的个数,从而快速计算总的元素个数。
  3. 记录元素出现次数的最大。

在此基础上我们对不同情况进行 分类讨论

  • 当最大出现次数为 1 的时候表示前缀中没有重复元素,删除任意一个元素即可。
  • 当最大出现次数的频次为 1,其余元素的出现次数都为次大频率,那么此时我们可以将最大出现次数的元素删除一个,使其出现的次数成为次大频率,那么此时数组中的元素出现的次数都为次大频率。
  • 除开一个数,其他所有数的出现次数都是最大出现次数,并且该数的出现次数为 1,那么可以直接删除出现次数为 1 的数,此时所有数的出现次数就都是最大出现次数了。

需要特别注意分清 元素出现次数元素出现次数的频次 这两个东西。

具体实现

使用哈希表 count 记录每个元素出现的次数,用 freq 记录每个出现次数的频次,用 maxCount 记录元素出现次数的最大值,也就是 count 中的最大值。

  • 遍历题目给定的数组 nums,假设当前访问的数为 nums[i],对应地更新 count[nums[i]]freq[count[nums[i]]] 以及 maxCount
  • 以下标 i 结尾的数组前缀符合要求的充要条件为满足以下三个条件之一,也就是 分类讨论 的三种情况:
    1. 最大出现次数 maxCount = 1:那么所有数的出现次数都是一次,随意删除一个数既可符合要求。
    2. 所有数的出现次数都是 maxCountmaxCount - 1,并且最大出现次数的数只有一个:删除一个最大出现次数的数,那么所有数的出现次数都是 maxCount − 1
    3. 除开一个数,其他所有数的出现次数都是 maxCount,并且该数的出现次数为 1:直接删除出现次数为 1 的数,那么所有数的出现次数都是 maxCount

优化

由于题目给定的数据范围为:[1,105][1, 10^5],那么我们可以将哈希表 countfreq 直接开成大小为 10510^5 的数组,这是因为数组的查询时间为标准的 O(1)O(1),而哈希表的查询数据为常数级别的 O(1)O(1),这样可以在一定程度上对时间复杂度进行优化。

复杂度分析

  • 时间复杂度:O(n)O(n),其中 n 是数组 nums 的长度。遍历数组 nums 需要 O(n)O(n)
  • 空间复杂度:O(n)O(n)。保存两个哈希表需要 O(n)O(n) 的空间。

代码实现

class Solution {
public:
    int maxEqualFreq(vector<int>& nums) {
        // maxCount 记录出现次数中的最大值,也就是 count 中的最大值
        int maxCount = 0, n = nums.size(), ans = 0;
        int count[100001], freq[100001];
        memset(count, 0, sizeof(count));
        memset(freq, 0, sizeof(freq));
        for(int i = 0; i < n; i++){
            // 如果 nums[i] 之前出现过
            if(count[nums[i]] > 0) freq[count[nums[i]]]--;
            // 增加 nums[i] 出现次数
            count[nums[i]]++;
            // 维护 maxCount 的值
            maxCount = max(maxCount, count[nums[i]]);
            // 维护频次
            freq[count[nums[i]]]++;
            // 
            bool check =
                // 最大出现次数为 1,任意删除一个数即可
                (maxCount == 1) ||
                // 最大出现次数的个数 + 次大出现次数的个数 = i + 1 并且 最大出现次数的个数为 1
                ((freq[maxCount] * maxCount + freq[maxCount - 1] * (maxCount - 1) == i + 1) 
                && (freq[maxCount] == 1)) ||
                // 最大出现次数的个数 + 1 = i + 1 并且出现次数为 1 次的有一个
                ((freq[maxCount] * maxCount + 1 == i + 1) && (freq[1] == 1));
            if(check) ans = i + 1;
        }
        return ans;
    }
};

总结

  • 该题有点套娃的感觉,比较绕和裹搅,需要理清 元素出现次数元素出现次数的频次 这两个东西。
  • 该题的难点在于 分类讨论 上,对于不同情况进行分类讨论,还要保证不重不漏是比较难的。
  • 测试结果:

1224. 最大相等频率 哈希表.png

1224. 最大相等频率.png 可以很明显的看到数组优化后在时间复杂度上的明显差异。

结束语

很多人遇到挫折时,经常会说:“如果当初怎样怎样,现在就不是这般场景了。”可时光无法倒流,没人能够改变过去。唯有把握好当下,才是对过往最好的答复。人生没有如果,愿你不纠结过去,不畏惧未来。新的一天,加油!