Given a non-empty array of non-negative integers nums
, the degree of this array is defined as the maximum frequency of any one of its elements.
Your task is to find the smallest possible length of a (contiguous) subarray of nums
, that has the same degree as nums
.
Example 1:
Input: [1, 2, 2, 3, 1] Output: 2 Explanation: The input array has a degree of 2 because both elements 1 and 2 appear twice. Of the subarrays that have the same degree: [1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2] The shortest length is 2. So return 2.
Example 2:
Input: [1,2,2,3,1,4,2] Output: 6
Note:
nums.length
will be between 1 and 50,000.nums[i]
will be an integer between 0 and 49,999.
這道題給了我們一個數組,定義數組的度為某個或某些數字出現最多的次數,要我們找最短的子數組使其和原數組擁有相同的度。那么我們肯定需要統計每個數字出現的次數,就要用哈希表來建立每個數字和其出現次數之間的映射。由於我們要求包含原度的最小長度的子數組,那么最好的情況就是子數組的首位數字都是統計度的數字,即出現最多的數字。那么我們肯定要知道該數字的第一次出現的位置和最后一次出現的位置,由於我們開始不知道哪些數字會出現最多次,所以我們統計所有數字的首尾出現位置,那么我們再用一個哈希表,建立每個數字和其首尾出現的位置。我們用變量degree來表示數組的度。好,現在我們遍歷原數組,累加當前數字出現的次數,當某個數字是第一次出現,那么我們用當前位置的來更新該數字出現的首尾位置,否則只更新尾位置。每遍歷一個數,我們都更新一下degree。當遍歷完成后,我們已經有了數組的度,還有每個數字首尾出現的位置,下面就來找出現次數為degree的數組,然后計算其首尾位置差加1就是candidate數組的長度,由於出現次數為degree的數字不一定只有一個,我們遍歷所有的,找出其中最小的即可,參見代碼如下:
解法一:
class Solution { public: int findShortestSubArray(vector<int>& nums) { int n = nums.size(), res = INT_MAX, degree = 0; unordered_map<int, int> m; unordered_map<int, pair<int, int>> pos; for (int i = 0; i < nums.size(); ++i) { if (++m[nums[i]] == 1) { pos[nums[i]] = {i, i}; } else { pos[nums[i]].second = i; } degree = max(degree, m[nums[i]]); } for (auto a : m) { if (degree == a.second) { res = min(res, pos[a.first].second - pos[a.first].first + 1); } } return res; } };
下面這種方法只用了一次遍歷,思路跟上面的解法很相似,還是要建立數字出現次數的哈希表,還有就是建立每個數字和其第一次出現位置之間的映射,那么我們當前遍歷的位置其實可以看作是尾位置,還是可以計算子數組的長度的。我們遍歷數組,累加當前數字出現的次數,如果某個數字是第一次出現,建立該數字和當前位置的映射,如果當前數字的出現次數等於degree時,當前位置為尾位置,首位置在startIdx中取的,二者做差加1來更新結果res;如果當前數字的出現次數大於degree,說明之前的結果代表的數字不是出現最多的,直接將結果res更新為當前數字的首尾差加1的長度,然后degree也更新為當前數字出現的次數。參見代碼如下:
解法二:
class Solution { public: int findShortestSubArray(vector<int>& nums) { int n = nums.size(), res = INT_MAX, degree = 0; unordered_map<int, int> m, startIdx; for (int i = 0; i < n; ++i) { ++m[nums[i]]; if (!startIdx.count(nums[i])) startIdx[nums[i]] = i; if (m[nums[i]] == degree) { res = min(res, i - startIdx[nums[i]] + 1); } else if (m[nums[i]] > degree) { res = i - startIdx[nums[i]] + 1; degree = m[nums[i]]; } } return res; } };
類似題目:
參考資料:
https://discuss.leetcode.com/topic/107097/java-o-n-time-o-n-space
https://discuss.leetcode.com/topic/107216/concise-c-solution-using-hash-map-o-n-time