Degree of an Array LT697
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.
Idea 1. Scan the array to build the frequency map, store the pair(nums[i], frequency), find out the max frequecy = degree, find out all candidates have the degree, build a second map with position to store the pari(nums[i], {start, end}}, loop the second map to find the max length end - start + 1.
Time complexity: O(N)
Space complexity: O(M) - unieque numbers in the array, could be O(N) if the degree is 1.
1 class Solution { 2 public int findShortestSubArray(int[] nums) { 3 Map<Integer, Integer> frequency = new HashMap<>(); 4 for(int num: nums) { 5 frequency.put(num, frequency.getOrDefault(num, 0) + 1); 6 } 7 8 int degree = 0; 9 for(int val: frequency.values()) { 10 degree = Math.max(degree, val); 11 } 12 13 Set<Integer> candidates = new HashSet<>(); 14 for(int key: frequency.keySet()) { 15 if(frequency.get(key) == degree) { 16 candidates.add(key); 17 } 18 } 19 20 Map<Integer, List<Integer>> start = new HashMap<>(); 21 for(int i = 0; i < nums.length; ++i) { 22 if(candidates.contains(nums[i])) { 23 start.putIfAbsent(nums[i], Arrays.asList(i, i)); 24 List<Integer> pos = start.get(nums[i]); 25 pos.set(1, i); 26 } 27 } 28 29 int result = nums.length; 30 for(List<Integer> pos: start.values()) { 31 result = Math.min(result, pos.get(1) - pos.get(0) + 1); 32 } 33 34 return result; 35 } 36 }
Let's start refactoring the code:
1. get the degree while building frequency map, saving the loop
2. save the start position of each number in start map, update the length as loop the array, note: each number could be the candidate, not just the number currently having same degree, as the counter could be increased as the same number occurs later
Time complexity: O(N), 1 scan only
Space complexity: O(M)
1 class Solution { 2 public int findShortestSubArray(int[] nums) { 3 int degree = 0; 4 int result = nums.length; 5 Map<Integer, Integer> frequency = new HashMap<>(); 6 Map<Integer, Integer> start = new HashMap<>(); 7 8 for(int i = 0; i < nums.length; ++i) { 9 frequency.put(nums[i], frequency.getOrDefault(nums[i], 0) + 1); 10 start.putIfAbsent(nums[i], i); 11 if(frequency.get(nums[i]) > degree) { 12 degree = frequency.get(nums[i]); 13 result = i - start.get(nums[i]) + 1; 14 } 15 else if(frequency.get(nums[i]) == degree) { 16 result = Math.min(result, i - start.get(nums[i]) + 1); 17 } 18 } 19 20 return result; 21 } 22 }