以下为本人思路,以及力扣的官方题解
题目
给定一个非空且只包含非负数的整数数组 n u m s nums nums,数组的度的定义是指数组里任一元素出现频数的最大值。
你的任务是在 n u m s nums nums 中找到与 n u m s nums nums 拥有相同大小的度的最短连续子数组,返回其长度。
示例1
输入: [ 1 , 2 , 2 , 3 , 1 ] [1, 2, 2, 3, 1] [1,2,2,3,1]
输出: 2 2 2
解释:输入数组的度是 2 2 2,因为元素 1 1 1 和 2 2 2 的出现频数最大,均为 2 2 2。
连续子数组里面拥有相同度的有如下所示:
[ 1 , 2 , 2 , 3 , 1 ] , [ 1 , 2 , 2 , 3 ] , [ 2 , 2 , 3 , 1 ] , [ 1 , 2 , 2 ] , [ 2 , 2 , 3 ] , [ 2 , 2 ] [1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [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,2] 的长度为 2 2 2,所以返回 2 2 2。
示例2
输入: [ 1 , 2 , 2 , 3 , 1 , 4 , 2 ] [1,2,2,3,1,4,2] [1,2,2,3,1,4,2]
输出: 6 6 6
提示
- n u m s . l e n g t h nums.length nums.length 在 1 1 1 到 50 , 000 50,000 50,000 区间范围内。
- n u m s [ i ] nums[i] nums[i] 是一个在 0 0 0 到 49 , 999 49,999 49,999 范围内的整数。
本人思路
设 x x x 为原数组中出现次数最多的数,则本题可理解为:找出包含原数组中所有 x x x 的最短子数组。且可以得出,该最短子数组的第一个元素以及最后一个元素必定为 x x x。
我们只需要将原数组各个元素出现的次数记录在数组 f f f 中,然后从中找到对应的 x x x。再利用左右指针 l e f t left left 和 r i g h t right right 分别向右、向左查找 x x x,找到的第一个 x x x 之后即停下,返回 r i g h t − l e f t + 1 right-left+1 right−left+1 的值。
需要注意的是,若原数组只有一个元素,则返回 1 1 1;若原数组中多个元素出现的次数均为最大次数,则需要分别求对应的子数组,再进行比较得到最短子数组。
失败代码
// 提交结果:解答错误
class Solution {
public int findShortestSubArray(int[] nums) {
int n = nums.length;
if (n == 1)
return 1;
int[] f = new int[50000];
int max = Integer.MIN_VALUE;
int[] index = new int[n];
int k = 0;
for (int i=0; i<n; i++)
{
f[nums[i]] ++;
}
for (int i=0; i<f.length; i++)
{
if (f[i] == 0)
continue;
if (max < f[i])
{
max = f[i];
}
}
for (int i=0; i<f.length; i++)
{
if (f[i] == 0)
continue;
if (max == f[i])
{
index[k] = i;
k++;
}
}
int m = 0;
int min = Integer.MAX_VALUE;
while (f[index[m]] > 0 && m < index.length-1)
{
int left = 0, right = 0;
for (int i=0; i<n; i++)
{
if (nums[i] == index[m])
{
left = i;
break;
}
}
for (int i=n-1; i>0; i--)
{
if (nums[i] == index[m])
{
right = i;
break;
}
}
min = Math.min(min, right-left+1);
m ++;
}
return min;
}
}
官方题解
方法一 哈希表
思路与算法
记原数组中出现次数最多的数为 x x x,那么和原数组的度相同的最短连续子数组,必然包含了原数组中的全部 x x x,且两端恰恰为 x x x 第一次出现和最后一次出现的位置。
因为符合条件的 x x x 可能有多个,即多个不同的数在原数组中出现次数相同。所以为了找到这个子数组,我们需要统计每一个数出现的次数,同时还需要统计每一个数第一次出现和最后一次出现的位置。
在实际代码中,我们使用哈希表实现该功能,每一个数映射到一个长度为 3 3 3 的数组,数组中的三个元素分别代表这个数出现的次数、这个数在原数组中第一次出现的位置和这个数在原数组中最后一次出现的位置。当我们记录完所有信息后,我们需要遍历该哈希表,找到元素出现次数最多,且前后位置差最小的数。
代码
class Solution {
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;
}
}
复杂度分析
- 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是原数组的长度,我们需要遍历原数组和哈希表各一次,它们的大小均为 O ( n ) O(n) O(n)。
- 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是原数组的长度,最坏情况下,哈希表和原数组等大。