题目
标题和出处
标题:数组的度
出处:697. 数组的度
难度
4 级
题目描述
要求
给定一个非空且只包含非负数的整数数组 nums \texttt{nums} nums,数组的度的定义是指数组中任一元素出现频数的最大值。
你的任务是在 nums \texttt{nums} nums 中找到与 nums \texttt{nums} nums 拥有相同大小的度的最短(连续)子数组的长度。
示例
示例 1:
输入:
nums
=
[1,2,2,3,1]
\texttt{nums = [1,2,2,3,1]}
nums = [1,2,2,3,1]
输出:
2
\texttt{2}
2
解释:
输入数组的度是
2
\texttt{2}
2,因为元素
1
\texttt{1}
1 和
2
\texttt{2}
2 都出现
2
\texttt{2}
2 次。
度相同的子数组如下:
[1,
2,
2,
3,
1],
[1,
2,
2,
3],
[2,
2,
3,
1],
[1,
2,
2],
[2,
2,
3],
[2,
2]
\texttt{[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
\texttt{2}
2,所以返回
2
\texttt{2}
2。
示例 2:
输入:
nums
=
[1,2,2,3,1,4,2]
\texttt{nums = [1,2,2,3,1,4,2]}
nums = [1,2,2,3,1,4,2]
输出:
6
\texttt{6}
6
解释:
数组的度是
3
\texttt{3}
3,因为元素
2
\texttt{2}
2 出现
3
\texttt{3}
3 次。
所以
[2,2,3,1,4,2]
\texttt{[2,2,3,1,4,2]}
[2,2,3,1,4,2] 是最短子数组,因此返回
6
\texttt{6}
6。
数据范围
- 1 ≤ nums.length ≤ 5 × 10 4 \texttt{1} \le \texttt{nums.length} \le \texttt{5} \times \texttt{10}^\texttt{4} 1≤nums.length≤5×104
- 0 ≤ nums[i] < 5 × 10 4 \texttt{0} \le \texttt{nums[i]} < \texttt{5} \times \texttt{10}^\texttt{4} 0≤nums[i]<5×104
解法
思路和算法
由于数组的度为数组中任一元素出现频数的最大值,数组中的任一元素的出现频数都不会超过数组的度。在与数组 nums \textit{nums} nums 拥有相同大小度的子数组中,一定至少包含一个出现频数等于数组的度的元素,且该元素在该子数组中的出现频数等于该元素在数组 nums \textit{nums} nums 中的出现频数,即该子数组包含数组 nums \textit{nums} nums 中的全部该元素。
为了找到与数组 nums \textit{nums} nums 拥有相同大小度的最短子数组的长度,需要记录每个元素在数组 nums \textit{nums} nums 中的出现频数,以及每个元素在数组 nums \textit{nums} nums 中的下标范围(即该元素第一次出现的下标和最后一次出现的下标)。使用两个哈希表分别记录出现频数和下标范围。
遍历数组 nums \textit{nums} nums,对于每个元素,分别更新两个哈希表,同时维护数组的度,如果当前元素的出现频数大于数组的度则将数组的度更新为当前元素的出现频数。遍历结束时,即可得到每个元素的出现频数和下标范围,以及数组的度。
由于数组 nums \textit{nums} nums 满足与其本身拥有相同大小的度,因此将最短子数组的长度初始化为数组 nums \textit{nums} nums 的长度。遍历哈希表中的每个元素,如果一个元素的出现频数等于数组的度,则根据该元素的下标范围计算包含全部该元素的最短子数组的长度(长度计算方法为该元素的最后一次出现的下标和第一次出现的下标之差加 1 1 1),并更新最短子数组的长度。遍历结束之后即可得到与数组 nums \textit{nums} nums 拥有相同大小度的最短子数组的长度。
也可以使用一个哈希表同时记录每个元素的出现频数和下标范围,和使用两个哈希表的做法是等价的。
代码
class Solution {
public int findShortestSubArray(int[] nums) {
int degree = 0;
Map<Integer, Integer> frequencyMap = new HashMap<Integer, Integer>();
Map<Integer, int[]> rangeMap = new HashMap<Integer, int[]>();
int length = nums.length;
for (int i = 0; i < length; i++) {
int num = nums[i];
int frequency = frequencyMap.getOrDefault(num, 0) + 1;
frequencyMap.put(num, frequency);
degree = Math.max(degree, frequency);
if (!rangeMap.containsKey(num)) {
rangeMap.put(num, new int[]{i, i});
} else {
rangeMap.get(num)[1] = i;
}
}
int minLength = length;
Set<Integer> set = frequencyMap.keySet();
for (int num : set) {
if (frequencyMap.get(num) == degree) {
int[] range = rangeMap.get(num);
minLength = Math.min(minLength, range[1] - range[0] + 1);
}
}
return minLength;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。需要遍历数组一次,使用两个哈希表分别记录每个元素出现频数和下标范围,然后遍历两个哈希表计算最短子数组的长度,由于哈希表中的元素个数不超过数组长度,因此时间复杂度是 O ( n ) O(n) O(n)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。需要使用两个哈希表分别记录每个元素出现频数和下标范围,两个哈希表中的元素个数都不会超过 n n n。