一、问题描述
给定一个非空且只包含非负数的整数数组 nums,数组的 度 的定义是指数组里任一元素出现频数的最大值。
二、示例及约束
示例 1:
输入: nums = [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 。
示例 2:
输入: nums = [1,2,2,3,1,4,2]
输出: 6
解释:
数组的度是 3 ,因为元素 2 重复出现 3 次。
所以 [2,2,3,1,4,2] 是最短子数组,因此返回 6 。
提示:
● 1 <= nums.length <= 50000
● 0 <= nums[i] <= 49999
三、代码
方法一:哈希表
class Solution {
public:
int findShortestSubArray(vector<int>& nums) {
unordered_map<int, vector<int>> mp;
int n = nums.size();
for (int i = 0; i < n; i++) {
if (mp.count(nums[i])) {
//count函数是unordered_map中的一个成员函数,它用于检查一个特定的键是否存在于映射中。如果存在,它返回 1(真);如果不存在,返回 0(假)。
//若存在
mp[nums[i]][0]++;//频数加1
mp[nums[i]][2] = i;//这个数最后一次出现的位置更改为i
} else {
//若不存在,即第一次扫描到这个数
mp[nums[i]] = {1, i, i};
//用三个元素来存储相应信息:第一个表示频数,第二个表示第一次该数出现的位置,第三个表示这个数最后出现的位置,初始化时设置为1,i,i
}
}
int maxNum = 0, minLen = 0;//初始化度值和最小长度值
for (auto& [_, vec] : mp) {
/*
auto& [_, vec] 表明想要绑定每个元素的键到一个不会使用的占位符 _,并将值绑定到 vec 变量上。
auto& 表示正在遍历 mp 的引用,而不是拷贝,在循环体内可以直接修改 mp 中的元素。
[_, vec] 是结构化绑定的语法,用于将每个键值对的键和值分别绑定到 _ 和 vec 上。在这里,_ 是一个占位符,表示不关心键的值,而 vec 绑定到当前迭代的值。
*/
if (maxNum < vec[0]) {
//遇到频数更大的数,更新信息
maxNum = vec[0];
minLen = vec[2] - vec[1] + 1;
} else if (maxNum == vec[0]) {
//如果两个频数相等,取更小的长度
if (minLen > vec[2] - vec[1] + 1) {
minLen = vec[2] - vec[1] + 1;
}
}
}
return minLen;
}
};
方法二:滑动窗口
class Solution {
public:
int findShortestSubArray(vector<int>& nums) {
int n = nums.size();
vector<int> freq(n, 0); //初始化频数数组
int degree = 0; //记录数组的度
//第一次遍历,记录每个数字出现的频数,并找到最大频数(度)
for (int i = 0; i < n; ++i) {
freq[nums[i]]++;
degree = max(degree, freq[nums[i]]);
}
fill(freq.begin(), freq.end(), 0); //重置频数数组
int left = 0, right = 0, minSpan = INT_MAX; //窗口边界和最小跨度
// 使用滑动窗口寻找最短子数组
while (right < n) {
freq[nums[right]]++; // 右窗口划进一个数,其频数加一
//如果当前数字的频数等于度,尝试收缩左窗口
while (left <= right && freq[nums[right]] == degree) {
minSpan = min(minSpan, right - left + 1); //记录最小窗口大小
freq[nums[left++]]--; //收缩左窗口
}
right++; //扩大右窗口
}
return minSpan;
}
};
四、总结
时间复杂度:
方法一:O(n),遍历数组和滑动窗口。
方法二:O(n),遍历数组和哈希表各一次。
空间复杂度:
方法一:O(n),最坏情况下,哈希表和原数组等大。
方法二:O(n),申请了一个大小为n的数组空间。
方法 | 时间复杂度 | 空间复杂度 |
---|---|---|
方法一 | O( n n n) | O(n) |
方法二 | O( n n n) | O(n) |