【LeetCode-697】数组的度
问题
给定一个非空且只包含非负数的整数数组 nums,数组的度的定义是指数组里任一元素出现频数的最大值。
你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。
提示:
- nums.length 在1到 50,000 区间范围内;
- nums[i] 是一个在 0 到 49,999 范围内的整数。
示例
输入: [1, 2, 2, 3, 1]
输出:2
解释:
输入数组的度是2,因为元素1和2的出现频数最大,均为2.
连续子数组里面拥有相同度的有如下所示:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
最短连续子数组[2, 2]的长度为2,所以返回2.
输入:[1,2,2,3,1,4,2]
输出:6
解答1:哈希表
class Solution {
public:
int findShortestSubArray(vector<int>& nums) {
int minLen = INT_MAX, degree = 0;
unordered_map<int, vector<int>> ump; // {最左坐标, 最右坐标, 出现次数}
for (int i = 0; i < nums.size(); i++) {
if (!ump.count(nums[i])) ump[nums[i]] = {i, i, 0};
else ump[nums[i]][1] = i;
ump[nums[i]][2]++;
}
for (auto& [_, v] : ump) {
if (degree == v[2]) minLen = min(minLen, v[1] - v[0] + 1);
else if (degree < v[2]) {
degree = v[2];
minLen = v[1] - v[0] + 1;
}
}
return minLen;
}
};
重点思路
使用一张哈希表存储数组中每个数的{最左坐标, 最右坐标, 出现次数}
。本题的三个需要注意的点:1.当哈希表的值为vector之类的结构时,判断其中是否存在数据只能使用ump.count(xxx)
这样的写法;2.对vector进行赋值之前,不要忘了初始化;3.遍历哈希表时,使用for (auto& [k, v] : ump)
这样的形式,或者for(auto& kv : ump)
然后使用kv.first
和kv.second
分别调用键与值。
本题首先遍历一遍数组,构建上述哈希表。再遍历一遍哈希表,确定频数最高数字的最小长度。
解答2:滑动窗口
class Solution {
public:
int findShortestSubArray(vector<int>& nums) {
int degree = 0, left = 0, right = 0, res = INT_MAX;
int memo[50000]{};
for (int i : nums)
degree = max(degree, ++memo[i]);
memset(memo, 0, sizeof memo);
while (right < nums.size()) {
memo[nums[right]]++;
while (memo[nums[right]] == degree) {
res = min(res, right - left + 1);
memo[nums[left++]]--;
}
right++;
}
return res;
}
};
重点思路
先求出degree,然后使用滑动窗口。具体做法为,当滑动窗口内存在长度为度的数时,开始收缩窗口,直到不满足为止。这个过程不会漏算长度更小的数,但是可能会漏算长度更大的数,例如[2, 1, 1, 2]
,滑动窗口根本不会统计2, 1, 1, 2
这一子数组,所以本题中滑动窗口法只能解决最短长度子数组的问题,如果要求刚好满足度的最长长度子数组,那么只能用解法1的哈希表算法。