697. Degree of an Array。

Given a non-empty array of non-negative integers nums, the degree of this array is defined as the maximum frequency of any one of its elements.

Your task is to find the smallest possible length of a (contiguous) subarray of nums, that has the same degree as nums.

Example 1:

Input: [1, 2, 2, 3, 1]
Output: 2
Explanation:
The input array has a degree of 2 because both elements 1 and 2 appear twice.
Of the subarrays that have the same degree:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
The shortest length is 2. So return 2.

Example 2:

Input: [1,2,2,3,1,4,2]
Output: 6

Note:

  • nums.length will be between 1 and 50,000.
  • nums[i] will be an integer between 0 and 49,999.

题中给定一个数组,其中元素出现最多的次数成为该数组的度数(degree ),然后对数组进行划分而且必须连续,要求划分的子数组的度和原数组的度一致,然后求出子数组最短的长度。


刚开始的思维就是,首先遍历一遍原数组找到数组的度数,并且找到生成该度数对应的数字,就如题中的例子: [1, 2, 2, 3, 1],对应的度数就是2,能够生成该度数对应的数字为:1,2。

然后再次遍历数组,找到度数对应数字出现时候的开始位置和结束位置,然后求出距离。就如题中的1,2,其中1对应的开始位置为:0,结束位置为:4;其中的2开始位置为:1,结束为止为2。所以最端的距离就是2-1+1。

class Solution {
public:
    int findShortestSubArray(vector<int>& nums) {
        int maxCount = 0;//数字出现的最多次数,也就是度数
        unordered_map<int,int> numCount;//用来存放每个数字出现的次数
        unordered_set<int> maxNums;//存放最大次数的数字

        for(int i=0;i<nums.size();i++) {
            numCount[nums[i]]++;//对应的次数加一

            if(numCount[nums[i]] > maxCount) {//交换位置,并清空maxNums数组,存放新的最大值
                maxCount = numCount[nums[i]];
                maxNums.clear();
                maxNums.insert(nums[i]);
            }
            if(numCount[nums[i]] == maxCount) {//如果相等的话,把新的数字添加进去
                maxNums.insert(nums[i]);
            }
        }

        int startIndex = -1;
        int endIndex = -1;
        int minLen = -1;

        for(int num : maxNums) {//遍历所有度数最大的数字,找到距离最短的
            startIndex = -1;
            endIndex = -1;
            for(int i=0; i<nums.size(); i++) {
                if(nums[i] == num) {
                    if(-1 == startIndex) {
                        startIndex = i;
                    }
                    if(i >= endIndex) {
                        endIndex = i;
                    }
                }
            }
            minLen = (minLen < 0) ? (endIndex-startIndex+1) : min(endIndex-startIndex+1,minLen);
            //cout << startIndex << "," << endIndex << endl;
            //cout << (endIndex-startIndex+1) << endl;
        }

        return minLen;
    }
};

上面的解题方法明显时间复杂度很高,而且从题中能够提取出关键的东西:数组的度数、每个元素的开始位置和结束位置。如果能够在时间复杂度为O(n)的情况下计算出关键的内容,就可以大大提高算法的性能。

仔细想一下,每个元素的开始位置是固定不变的。结束的位置是会发生变换的,但是在循环的过程中如果使用for(int i=0;i < n; i ++)这种形式的话,那么i就可以作为结束的位置的。同样的数组的度数在循环的过程中也是可以变换的,度数的更新是跟每个元素的出现次数有关的。所以我们只需要记录下元素的开始位置和出现的次数,然后利用循环时的i和开始位置就能够算出距离,再利用元素出现的次数实时更新度数。最后就能够在时间复杂度为O(n)的情况下算出结果。

class Solution {
public:
    int findShortestSubArray(vector<int>& nums) {
        if(nums.size() <= 1) {
            return nums.size();
        }
        int minLen = 0;//最小的为1
        int degree = 0;//最小的度为1

        unordered_map<int,pair<int,int>> ans;//用来存储元素和该元素开始下标和出现的次数

        for(int i=0; i<nums.size(); i++) {
            ans[nums[i]].second++;//出现的次数加一
            if(ans[nums[i]].second == 1) {//代表该元素首次出现
                //cout << "数字:" << nums[i] << ",开始的坐标" << i << endl;
                ans[nums[i]].first = i;//记录开始的坐标
            }

            if(ans[nums[i]].second > degree) {//如果此时出现的次数大于数组中的最高度数
                degree = ans[nums[i]].second;//重置最高度数
                minLen = i - ans[nums[i]].first + 1;//当前位置减去开始位置得到中间长度
                //cout << "当前坐标" << i << ",开始坐标:" << ans[nums[i]].first << endl;
            } else if(ans[nums[i]].second == degree) {//如果度数相等则需要比较哪一个比较短
                minLen = min(minLen,i - ans[nums[i]].first + 1);
            }
        }

        return minLen;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值