写在前面
虽然碰到过很多次这种需要大量额外空间的题,但是看到题的第一反应总也不会往那个方向考虑。这是一道简单题,难度确实不大,但是选对使用什么数据结构、使用多少个总是不那么容易的,还是需要慢慢培养这种思路诶。
题目
核心思路
直接法/暴力法(超时)
就像最开始说的,因为看到题并没有想到使用3个map这么多,所以第一次提交使用的直接法,超时也是理所应当的。这道题直接法思路简单,先实现一个函数计算指定范围数组的度,然后先求出nums的度(degree),之后遍历数组的每一种可能的范围,如果度与degree相等,所求的最短子数组长度就是当前遍历到的长度与先前最短长度的最小值。遍历数组的复杂度为O(n²),求度又要O(n)的复杂度,所以总的复杂度很大,具体的代码就不给出了。
3个map3次遍历
其实根据题目的意思,数组的度只与频率最高的数有关,所以没必要求每个子数组的度,只要找到频率最高的数第一次出现和最后一次出现的位置即可,由于频率最高的数可能不止一个,所以再遍历一次求出最小值即可。所以使用3个map,键为数组元素的值,第一个map的值存储该元素出现的次数,第二个map存储该元素第一次出现的位置,第三个map存储该元素最后一次出现的位置。
完整代码
class Solution {
public int findShortestSubArray(int[] nums) {
HashMap<Integer, Integer> left = new HashMap(), right = new HashMap(), count = new HashMap();
//count存储次数、left存储第一次位置、right存储最后一次出现的位置
int maxDegree = 0;//数组的度
for (int i = 0; i < nums.length; i++) {
if (count.containsKey(nums[i])) {
count.put(nums[i], count.get(nums[i]) + 1);
} else {
count.put(nums[i], 1);
left.put(nums[i], i);//第一次出现时存入left
}
maxDegree = Math.max(maxDegree, count.get(nums[i]));
}
for (int i = nums.length - 1; i >= 0; i--) {
if (!right.containsKey(nums[i])) {
right.put(nums[i], i);//倒序遍历第一次出现即为最后一次出现的位置
}
}
int ans = nums.length;//所求的子数组最大为nums的长度
for (int i : count.keySet()) {
if (count.get(i) == maxDegree) {
ans = Math.min(ans, right.get(i) - left.get(i) + 1);
}
}
return ans;
}
}
这道题给我的教训主要是不要吝惜空间,有需要就用,太过扣的话就很容易做不出优秀的题解;当然了,实际使用中时间和空间的权衡也是一项比较重要的问题,需要学习的东西还是很多啊。
网友评论