No.594 最长和谐子序列。难度:简单和谐数组是指一个数组里元素的最大值和最小值之间的差别正好是1。
现在,给定一个整数数组,你需要在所有可能的子序列中找到最长的和谐子序列的长度。
示例 1:
输入:
[1,3,2,2,5,2,3,7]
输出:
5
原因: 最长的和谐数组是:
[3,2,2,2,3]
.说明: 输入的数组长度最大不超过
20,000
.
思路一
关键点
- 看到数组长度不超过
20,000
,说明穷举是肯定不会超时的 - 只需要输出长度,意味着不需要记录子数组
- 从示例来看,是不需要连续的
- 差别正好是
1
,可以先设定一个数为n
,去遍历n
和n+1
的个数
实现
public int findLHS(int[] nums) {
int max = 0;
for(int i = 0; i < nums.length; i++){
int num = 0;
boolean diff = false;
for(int j = 0; j < nums.length; j++){
if(nums[i] == nums[j] || nums[i] + 1 == nums[j]){
num++;
if(nums[i] != nums[j]){
diff = true; // 如果数组全是一样的值,不能满足题目对于最大最小值正好相差1的描述
}
}
}
if(diff && num > max){
max = num;
}
}
return max;
}
时间复杂度$O(N^2)$,意料之中的垫底。
思路二
关键点
- 遍历一次,将每个数字出现的次数记录在
HashMap
中 - 假设遍历的数字为
n
,写入HashMap
的时候查看n-1
和n+1
的出现次数,并取最大值
实现
public int findLHS(int[] nums) {
int max = 0;
Map<Integer, Integer> map = new HashMap<>();
for(int i = 0; i < nums.length; i++){
map.put(nums[i], map.getOrDefault(nums[i], 0) + 1);
if(map.containsKey(nums[i] - 1)){
max = Math.max(max, map.get(nums[i]) + map.get(nums[i] - 1));
}
if(map.containsKey(nums[i] + 1)){
max = Math.max(max, map.get(nums[i]) + map.get(nums[i] + 1));
}
}
return max;
}
时间复杂度$O(N)$,可是结果...
思路三
关键点
- 大佬真多
- 先进行排序,这样使用双指针就能进行单遍历了
- 相差为
0
和1
时endIndex
一直往后移动,超过1
时把startIndex
跳到上次endIndex
增加的地方
实现
public int findLHS(int[] nums) {
Arrays.sort(nums);
int startIndex = 0;
int endIndex = 0;
int max = 0;
int lastDiffIndex = 1;
while(endIndex < nums.length){
int start = nums[startIndex];
int end = nums[endIndex];
if(end - start == 0){
endIndex++;
}else if(end - start == 1){
if(nums[lastDiffIndex] != end){
lastDiffIndex = endIndex;
}
max = Math.max(max, endIndex - startIndex + 1);
endIndex++;
}else{
startIndex = lastDiffIndex;
lastDiffIndex = endIndex;
}
}
return max;
}
时间复杂度也是$O(N)$,但是却少了很多操作HashMap
的时间和判断次数。
结语
算法就是要追求极致,如果有更好更优美的实现方式,或者文中出现了错误,欢迎留言交流指正。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。