美文网首页
2021.2.20每日一题

2021.2.20每日一题

作者: Yaan9 | 来源:发表于2021-02-20 09:53 被阅读0次

    697. 数组的度

    给定一个非空且只包含非负数的整数数组 nums,数组的度的定义是指数组里任一元素出现频数的最大值。
    你的任务是在 nums 中找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。
    示例 1:

    输入:[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:

    输入:[1,2,2,3,1,4,2]
    输出:6
    

    题解

    维护两个HashMap,一个用于记录nums数组中的最大频率,另一个用于记录当前子数组中的最大频率。使用了滑动窗口的思想。初始不断向右扩张窗口,当发现当前窗口的度和数组的度相同时,窗口左边开始收缩,在收缩的过程中要不断更新窗口的度。总体和滑动窗口模板很类似。

        public int findShortestSubArray(int[] nums) {
            int n = nums.length;
            int left = 0;
            int right = 0;
            int degree = 0; //数组的度
            int curDegree = 0; //当前窗口的度
            int res = Integer.MAX_VALUE;
            HashMap<Integer, Integer> map1 = new HashMap<>();
            HashMap<Integer, Integer> map2 = new HashMap<>();
            for (int i = 0; i < n; i++) {
                map1.put(nums[i], map1.getOrDefault(nums[i], 0) + 1);
                degree = Math.max(degree, map1.get(nums[i]));
            }
            while (right < n) {
                map2.put(nums[right], map2.getOrDefault(nums[right], 0) + 1);
                curDegree = Math.max(curDegree, map2.get(nums[right]));
                while (curDegree == degree && left <= right) { //循环结束的条件还要有left>right
                    res = Math.min(res, right - left + 1);
                    int oldValue = map2.get(nums[left]); 
                    map2.put(nums[left], --oldValue);
                    if (map2.get(nums[left]) == curDegree - 1) curDegree = map2.get(nums[left]); //如果收缩后窗口的度减少,要更新
                    left++;
                }
                right++;
            }
            return res;
        }
    

    还有一种解法是官解:记原数组中出现次数最多的数为 x,那么和原数组的度相同的最短连续子数组,必然包含了原数组中的全部 x,且两端恰为 x 第一次出现和最后一次出现的位置。
    因为符合条件的 x 可能有多个,即多个不同的数在原数组中出现次数相同。所以为了找到这个子数组,我们需要统计每一个数出现的次数,同时还需要统计每一个数第一次出现和最后一次出现的位置。
    在实际代码中,我们使用哈希表实现该功能,每一个数映射到一个长度为 3 的数组,数组中的三个元素分别代表这个数出现的次数、这个数在原数组中第一次出现的位置和这个数在原数组中最后一次出现的位置。当我们记录完所有信息后,我们需要遍历该哈希表,找到元素出现次数最多,且前后位置差最小的数。
    官解

        public int findShortestSubArray(int[] nums) {
            Map<Integer, int[]> map = new HashMap<Integer, int[]>();
            int n = nums.length;
            for (int i = 0; i < n; i++) {
                if (map.containsKey(nums[i])) {
                    map.get(nums[i])[0]++;
                    map.get(nums[i])[2] = i;
                } else {
                    map.put(nums[i], new int[]{1, i, i});
                }
            }
            int maxNum = 0, minLen = 0;
            for (Map.Entry<Integer, int[]> entry : map.entrySet()) {
                int[] arr = entry.getValue();
                if (maxNum < arr[0]) {
                    maxNum = arr[0];
                    minLen = arr[2] - arr[1] + 1;
                } else if (maxNum == arr[0]) {
                    if (minLen > arr[2] - arr[1] + 1) {
                        minLen = arr[2] - arr[1] + 1;
                    }
                }
            }
            return minLen;
        }
    

    相关文章

      网友评论

          本文标题:2021.2.20每日一题

          本文链接:https://www.haomeiwen.com/subject/xtdtfltx.html