We define a harmonious array is an array where the difference between its maximum value and its minimum value is exactly 1.
Now, given an integer array, you need to find the length of its longest harmonious subsequence among all its possible subsequences.
Example 1:
Input: [1,3,2,2,5,2,3,7] Output: 5 Explanation: The longest harmonious subsequence is [3,2,2,2,3].
Note: The length of the input array will not exceed 20,000.
需要注意的是,这道题的意思是:最大值与最小值的差 正好 是1,而不是小于等于1。比如测试用例:结果就应该是0。
下面是我的解法。
public int findLHS(int[] nums) {
if(nums.length<=1){
return 0;
}
//map中存储某个数出现了多少次
HashMap<Integer, Integer> preCount=new HashMap<Integer, Integer>();
int maxLength=0;
for(int i=0;i<nums.length;i++){
int number=nums[i];
int numberCount=preCount.getOrDefault(number, 0);
//拿到number-1和number+1迄今为止出现的次数
int numberMinus1Count=preCount.getOrDefault(number-1, 0);
int numberPlus1Count=preCount.getOrDefault(number+1, 0);
if(numberMinus1Count!=0 || numberPlus1Count!=0){
int nowMax=1+numberCount+Math.max(numberMinus1Count, numberPlus1Count);
if(nowMax>maxLength){
maxLength=nowMax;
}
}
preCount.put(number, numberCount+1);
}
return maxLength;
}
大神也用hashmap,不过方法有点不同。
思路是得到每个数字的出现个数,然后再检查每个数字的+1元素的个数。
public int findLHS(int[] nums) {
Map<Long, Integer> map = new HashMap<>();
for (long num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
int result = 0;
for (long key : map.keySet()) {
if (map.containsKey(key + 1)) {
result = Math.max(result, map.get(key + 1) + map.get(key));
}
}
return result;
}
使用long而不是int的原因是防止出现测试用例是:new int[]{ -2147483648 , 2147483647} 的情况,因为2147483647 + 1 = -2147483648,不是我们希望的结果。
并且,不需要同时检查
key+1
和
key-1
, 因为假设在一个有效答案中有 key
和 key-1
, 那么在以 key-1
作为 key 时就能够得到这个答案。
这道题有solutions:https://leetcode.com/problems/longest-harmonious-subsequence/solution/。solutions中的解法我基本上面两个都提到了。
Approach #3 Using Sorting [Accepted]
Algorithm
Since we are concerned only with the count of elements which are at a difference of 1, we can use sorting to our advantage. If we sort the given nums array, the related elements will get arranged close to each other. Thus, we can traverse over the sorted array, and find the count of similar elements and elements one larger than the current ones, which occur consecutively(all the similar elements will be lying consecutively now). Initially, this value is stored in prev_countvariable. Then, if we encounter an element which is just 1 larger than the last elements, we count the occurences of such elements as well. This value is stored in count variable.
Thus, now for the harmonic subsequence comprised of only these two elements is a subsequence of length count+prev_count. This result is stored in resfor each subsequence found. When we move forward to considering the next set of similar consecutive elements, we need to update the prev_count with the count's value, since now count will act as the count of the elements 1 lesser than the next elements encountered. The value of res is always updated to be the larger of previous res and the current count+prev_count value.
When we are done traversing over the whole array, the value of res gives us the required result.
Java
public class Solution { public int findLHS(int[] nums) { Arrays.sort(nums); int prev_count = 1, res = 0; for (int i = 0; i < nums.length; i++) { int count = 1; if (i > 0 && nums[i] - nums[i - 1] == 1) { while (i < nums.length - 1 && nums[i] == nums[i + 1]) { count++; i++; } res = Math.max(res, count + prev_count); prev_count = count; } else { while (i < nums.length - 1 && nums[i] == nums[i + 1]) { count++; i++; } prev_count = count; } } return res; } }
Complexity Analysis
-
Time complexity : O(nlogn). Sorting takes O(nlogn) time.
-
Space complexity : O(logn). logn space is required by sorting in average case.
Approach #4 Using HashMap[Accepted]:
Algorithm
In this approach, we make use of a hashmap map which stores the number of times an element occurs in the array along with the element's value in the form (num:count_num), where num refers to an element in the array and count_num refers to the number of times this num occurs in the nums array. We traverse over the nums array and fill this map once.
After this, we traverse over the keys of the map created. For every key of the map considered, say key, we find out if the map contains the key+1. Such an element is found, since only such elements can be counted for the harmonic subsequence if key is considered as one of the element of the harmonic subsequence. We need not care about key−1, because if key is present in the harmonic subsequence, at one time either key+1 or key−1 only could be included in the harmonic subsequence. The case of key−1 being in the harmonic subsequence will automatically be considered, when key−1 is encountered as the current key.
Now, whenver we find that key+1 exists in the keys of map, we determine the count of the current harmonic subsequence as countkey+countkey+1, where counti refers to the value corresponding to the key i in map, which reprents the number of times i occurs in the array nums.
Look at the animation below for a pictorial view of the process:
Java
public class Solution { public int findLHS(int[] nums) { HashMap < Integer, Integer > map = new HashMap < > (); int res = 0; for (int num: nums) { map.put(num, map.getOrDefault(num, 0) + 1); } for (int key: map.keySet()) { if (map.containsKey(key + 1)) res = Math.max(res, map.get(key) + map.get(key + 1)); } return res; } }
Complexity Analysis
-
Time complexity : O(n). One loop is required to fill map and one for traversing the map.
-
Space complexity : O(n). In worst case map size grows upto size n.
Approach #5 In single loop [Accepted]:
Algorithm
Instead of filling the map first and then traversing over the map to determine the lengths of the harmonic subsequences encountered, we can traverse over the nums array, and while doing the traversals, we can determine the lengths of the harmonic subsequences possible till the current index of the nums array.
The method of finding the length of harmonic subsequence remains the same as the last approach. But, this time, we need to consider the existence of both key+1 and key−1 exclusively and determine the counts corresponding to both the cases. This is needed now because it could be possible that key has already been added to the map and later on key−1 is encountered. In this case, if we consider the presence of key+1 only, we'll go in the wrong direction.
Thus, we consider the counts corresponding to both the cases separately for every key and determine the maximum out of them. Thus, now the same task can be done only in a single traveral of the nums array.
See the animation below for understanding the process:
Java
public class Solution { public int findLHS(int[] nums) { HashMap < Integer, Integer > map = new HashMap < > (); int res = 0; for (int num: nums) { map.put(num, map.getOrDefault(num, 0) + 1); if (map.containsKey(num + 1)) res = Math.max(res, map.get(num) + map.get(num + 1)); if (map.containsKey(num - 1)) res = Math.max(res, map.get(num) + map.get(num - 1)); } return res; } }
Complexity Analysis
-
Time complexity : O(n). Only one loop is there.
-
Space complexity : O(n). map size grows upto size n.