文章目录
😎哈希表解决最短连续子数组😎

1.题目分析
数组的度定义为数组中任一元素出现频数的最大值,也就是数组中出现次数最多的那个数。
我们的任务是:求出nums
数组中与nums
拥有相同大小度的连续子数组,并且使这个子数组的长度尽可能的短,最终返回该子数组的长度。
2.哈希表的结构设计
了解题目的要求过后,我们就可以把这个题目分解成两个子问题:找到数组的度
、再找到最短连续子数组
。
找到数组的度很容易联想到使用哈希表来解决,只需要利用哈希表来记录每个数出现的次数,再遍历哈希表找到最多的次数即可。🤞困难之处在于如何找到最短连续子数组🤞。
我们先来分析一下最短连续子数组的需求:假设数组nums
中出现次数最多的数是n
,nums
的度是x
,那么最短子数组将会是:数字n
在nums
中第一次出现的位置和最后一次出现的位置之间所有元素所构成的数组。
通过以上分析不难发现,我们不仅仅要保存元素出现的次数,还要保存元素第一次出现的索引和最后一次出现的索引,以便求得连续子数组的长度。综上,😘我们可以设计出这样的哈希表:每一个数映射到一个长度为3的数组,数组中的三个元素分别为元素出啊先的次数、元素在原数组中第一次出现的位置 和 元素在原数组中最后一次出现的位置。😘
👏这里还有一种特殊情况👏,就是:出现次数最多的数字不止一个,那么我们就需要比较它们所构成的最短连续子数组的长度,然后选取长度最短的那一个。
3.Java的Map遍历方式
当把元素全部存入map后,我们需要遍历map来得到数组的度 和 最小连续子数组的长度。我个人对Map的遍历方式掌握的不是特别牢固,这里再复习一下。
3.1 Map.Entry
介绍Map的遍历之前,我们先来聊聊Map.Entry
这个类,它是Map中的一个内部类,表示一个映射项,映射项中包含key
和value
(也就是我们常说的键值对,每一个键值对也就是一个Entry对象)。Map.Entry类中有getKey()
和getVallue()
提供给我们获取对应的key和value😜
3.2 Map.entrySet()
它是Map类的一个方法,它返回的值就是所有键值对所组成的集合,Set里面的类型是Map.Entry。这样我们通过Map.entrySet()这个方法就可以获取到所有的键值对集合,遍历这个Set集合进而达到遍历Map的目的。
3.3 4种方法遍历Map
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
//第一种:普遍使用,二次取值
System.out.println("通过Map.keySet遍历key和value:");
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
//第二种
System.out.println("通过Map.entrySet使用iterator遍历key和value:");
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第三种:推荐,尤其是容量大时
System.out.println("通过Map.entrySet遍历key和value");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第四种
System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}
4.源码
4.1 我的第一遍解法
1.找到数组中的众数
2.找到各个众数对应的连续子串
3.找出最短的那一个
public int findShortestSubArray(int[] nums) {
HashMap<Integer, Integer> map = new HashMap<>();
int max = -1, min = nums.length + 1;
ArrayList<Integer> list = new ArrayList<>();
//1.找到众数
for (int num : nums) {
Integer integer = map.get(num);
if (integer == null) {
map.put(num, 1);
} else {
map.replace(num, integer + 1);
}
}
for (Integer integer : map.keySet()) {
Integer count = map.get(integer);
if (count == max) {
list.add(integer);
}
if (count > max) {
max = count;
list.clear();
list.add(integer);
}
}
//找到最短的那个连续子串
List<Integer> numsList = Arrays.stream(nums).boxed().collect(Collectors.toList());
for (Integer integer : list) {
int l = numsList.indexOf(integer);
int r = numsList.lastIndexOf(integer);
int length = r - l + 1;
if (min > length) {
min = length;
}
}
return min;
}
4.2 最优解
public int findShortestSubArray(int[] nums) {
HashMap<Integer, int[]> hashMap = new HashMap<>();
//构建HashMap
for (int i = 0; i < nums.length; i++) {
int num = nums[i];
if (hashMap.containsKey(num)) {
//如果已经存过,个数加1并更新最后出现的位置
hashMap.get(num)[0]++;
hashMap.get(num)[2] = i;
} else {
//如果没有存过,初始化个数为1,第一次和最后一次出现的位置初始化为i
hashMap.put(num, new int[]{1, i, i});
}
}
int maxDegree = -1, minLength = -1;
//找到最短的子串
for (Map.Entry<Integer, int[]> entry : hashMap.entrySet()) {
int[] arr = entry.getValue();
if(arr[0]>maxDegree){
maxDegree= arr[0];
//minLength更新为当前这个元素所构造的最短子数组的长度
minLength= arr[2]- arr[1]+1;
}else if(arr[0]==maxDegree){
//如果出现了频数一样的元素(特殊情况),那么取子数组最短的那个
minLength= Math.min(minLength, arr[2] - arr[1] + 1);
}
}
return minLength;
}