這是悅樂書的第294次更新,第312篇原創
01 看題和準備
今天介紹的是LeetCode算法題中Easy級別的第162題(順位題號是697)。給定一個由正整數組成的非空數組,該數組的度數被定義爲任意元素出現次數最多的次數。你的任務是找到一個(連續的)nums子數組的最小可能長度,它與nums具有相同的度數。例如:
輸入:[1,2,2,3,1]
輸出:2
說明:輸入數組的度數爲2,因爲元素1和2都出現兩次。在具有相同程度的子陣列中:[1,2,2,3,1],[1,2,2,3],[2,2,3,1],[1,2,2],[2,2,3],[2,2],最短的長度是2.所以返回2。
輸入:[1,2,2,3,1,4,2]
輸出:6
注意:
數組長度將介於1到50,000之間。
數組中的元素是0到49,999之間的整數。
本次解題使用的開發工具是eclipse,jdk使用的版本是1.8,環境是win7 64位系統,使用Java語言編寫和測試。
02 第一種解法
按照題目的意思,我們需要先把出現次數最多的元素找出來,然後再找到該元素第一次出現和最後一次出現的索引位置之間的距離,如果存在多個相同最多次數的元素,還需要比較他們之間的最小值。
因此,第一步,將數組元素存入HashMap,得到最大次數;第二步,將出現最多次數的單個或多個元素添加進數組;第三步,找到新數組中每位元素出現的起始位置,計算距離長度,取其中的較小者。
public int findShortestSubArray(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
Map<Integer,Integer> map = new HashMap<Integer, Integer>();
int degree = 0;
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0)+1);
// 比較出現次數,取其中較大者
degree = Math.max(degree, map.get(num));
}
// 將最多出現次數相同的元素放入新的數組
int[] occur = new int[nums.length];
int index = 0;
for (Integer key : map.keySet()) {
if (map.get(key) == degree) {
occur[index++] = key;
}
}
int min = Integer.MAX_VALUE;
// 遍歷新數組,算出出現次數最多的元素在nums中的索引之差
for (int i=0; i< index; i++) {
// 使用雙指針,一前一後遍歷nums,找到該元素的索引之差
int start = 0, end = nums.length-1;
// 申明一個變量,存儲索引之差
int len = Integer.MAX_VALUE;;
while (start <= end) {
if (occur[i] == nums[start] && occur[i] == nums[end]) {
len = end - start +1;
break;
} else if (occur[i] == nums[start] && occur[i] != nums[end]){
end--;
} else if(occur[i] != nums[start] && occur[i] == nums[end]) {
start++;
} else {
end--;
start++;
}
}
// 取兩者之間的最小值
min = Math.min(min, len);
}
return min;
}
03 第二種解法
我們也可以不使用新數組來存那些出現最多的元素,將HashMap的value類型改爲數組即可。依舊使用數組中的元素作爲key,value變成了一個包含三個元素的數組,存儲次數、首次出現索引、最後一次出現索引,依舊是在第一次循環中就得到degree的值,然後遍歷HashMap的value,找到元素索引間隔最小的那個。
public int findShortestSubArray2(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int degree = 0;
// 以nums中的元素爲key,以一個含有3個元素的數組爲value
// 該數組第一個元素爲當前元素出現的次數,第二個元素爲其第一次出現的位置,第三個元素爲其最後一次出現的位置
Map<Integer,int[]> map = new HashMap<Integer, int[]>();
for (int i=0; i<nums.length; i++) {
if (map.containsKey(nums[i])) {
int[] temp = map.get(nums[i]);
// 更新該元素出現次數
temp[0]++;
// 更新該元素最後一次出現的位置(索引)
temp[2] = i;
} else {
map.put(nums[i], new int[]{1, i, i});
}
// 更新degree,取兩者之間較大值
degree = Math.max(degree, map.get(nums[i])[0]);
}
int min = Integer.MAX_VALUE;
for (int[] values : map.values()) {
if (values[0] == degree) {
min = Math.min(min, values[2] - values[1] + 1);
}
}
return min;
}
04 第三種解法
對於第二種解法,我們可以再簡化下,只使用一個循環來處理。
public int findShortestSubArray3(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int degree = 0;
int min = Integer.MAX_VALUE;
// 以nums中的元素爲key,以一個含有2個元素的數組爲value
// 該數組第一個元素爲當前元素出現的次數,第二個元素爲其最後一次出現的位置
Map<Integer,int[]> map = new HashMap<Integer, int[]>();
for (int i=0; i<nums.length; i++) {
if (!map.containsKey(nums[i])) {
map.put(nums[i], new int[]{0, i});
}
// 出現次數累加
map.get(nums[i])[0]++;
// 對degree進行處理
if (degree < map.get(nums[i])[0]) {
degree = map.get(nums[i])[0];
min = i - map.get(nums[i])[1] + 1;
} else if (degree == map.get(nums[i])[0]) {
min = Math.min(min, i - map.get(nums[i])[1] + 1);
}
}
return min;
}
05 第四種解法
我們也可以使用兩個HashMap,而不使用數組。第一個HashMap的value存每個元素出現的次數,第二個HashMap的value存每個元素出現的初始位置索引。
在循環中,每次去比較第一個HashMap的value,如果比degree大,就更新degree的值,同時算出該元素起始索引和結束索引之間的距離;如果和degree相等,就在兩者之間取較小值。
public int findShortestSubArray4(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
int degree = 0;
int min = Integer.MAX_VALUE;
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Map<Integer, Integer> map2 = new HashMap<Integer, Integer>();
for (int i=0; i<nums.length; i++) {
map.put(nums[i], map.getOrDefault(nums[i], 0)+1);
if (!map2.containsKey(nums[i])) {
map2.put(nums[i], i);
}
if (degree < map.get(nums[i])) {
degree = map.get(nums[i]);
min = i - map2.get(nums[i]) + 1;
} else if (degree == map.get(nums[i])) {
min = Math.min(min, i - map2.get(nums[i]) + 1);
}
}
return min;
}
06 小結
算法專題目前已日更超過四個月,算法題文章162+篇,公衆號對話框回覆【數據結構與算法】、【算法】、【數據結構】中的任一關鍵詞,獲取系列文章合集。
以上就是全部內容,如果大家有什麼好的解法思路、建議或者其他問題,可以下方留言交流,點贊、留言、轉發就是對我最大的回報和支持!