Leetcode 697 数组的度

一、问题描述

  给定一个非空且只包含非负数的整数数组 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)
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值