题目大意
给定一组数字,返回最长的连续数字长度
思路
此题是个很不错的题目,可以不断地压缩复杂度,很能锻炼思维。首先最容易想到的方法就是先对数组排序,然后遍历数组,记录长度后找到最大值(方法一)。这个时间复杂度是O(n*logn),leetcode上使用这种方法是可以水过的(亲测100ms),显然这种方法不符合题目要求的O(n)时间复杂度。
由于方法一的时间主要花费在排序上,那么题目就不允许排序操作,很自然地会想到hash,想到hash不难,难的是如何使用hash去降低时间复杂度,我使用两个hashmap:mp_ele记录(当前数,当前数+1)的映射、mp_visited记录(当前数,当前数是否被访问),这样我遍历数组元素num[i]时,若此数未被访问过,我就反复查找其value值、value的value是否存在.....这样循环下去直到不存在在hash中,记录这个循环查找的长度,然后从中找出最大值(方法二),原本以为这是一个O(n)的方法,因为查找过程中会将查找过的值舍去,但提交结果是TLE,仔细一想,发现这个对于顺序才是O(n)如果是逆序就会上升到O(n^2),实在是太不划算,费空间又费时间。但是一直没有想到任何能够同时照顾+1和-1两个方向的hash方法。
既然hash走不通,就想想别的空间耗费方法,看到leetcode那组TLE数据元素大小最大也才不到1000,因此就想到了一个tricky的方法,找出数组中最大值和最小值,建立一个下标从最小值到最大值的bool数组(对于负数情况,只需要下标加上最小的负数值就行了),然后将出现的值都赋值为true,同时记录长度,返回最大值(方法三)。心想着这下时间复杂度和空间复杂度是O(max(a[i])-min(a[i])),应该可以水过。。。结果。。。只能说leetcode数据质量确实不错,短小精悍,在经历了这样一组数据(Last executed input:[2147483646,-2147483647,0,2,2147483644,-2147483645,2147483645])后。。。%#&*$¥。。。又再次成功地TLE了。
在连续跪在hash和线性数组上之后,思考了一下线段树的可能(方法四),后来发现线段树虽然时间上应该可以降到(n*logn),但空间消耗过于巨大,而且代码又复杂,时间复杂度也没到O(n)就没有尝试这种方法了。
实在对此题无力,最后还是参考了一下discussion大神们的方法,发现他们很好地利用了hashmap键和值分别作为边界(两者都有可能是上下界),并且保证只需要在当前数num[i]附近增减1位就能找到之前出现过的最大值和最小值,从而计算出长度。
总结
1、判断map中是否存在数要使用其自带的find函数(除非保证键值映射没有0,这样才能使用if(map[key]))来判断是否存在
2、为保证代码清晰自然,可以多使用几个变量保存一些值,这点消耗是非常值得的,不然会很难分析清楚自己的代码
3、写完代码可以手动跑几个样例来避免一些不必要的错误,此时不能浮躁。
代码(附测试)
#include<iostream>
#include<map>
#include<vector>
using namespace std;
int longestConsecutive(vector<int> &num)
{
map<int, int> mp_bound;//记录上边界或者下边界
mp_bound.clear();
int max_len = 1;
for (int i = 0; i < num.size(); i ++){
if (mp_bound.find(num[i]) == mp_bound.end()){//这里不要用if(mp_bound[num[i]])
mp_bound[num[i]] = num[i];//由于重复的num[i]对hashmap没有影响,这里mp_bound只需要赋值为自己即可,随后的数据才会对其形成新的更改
int low = num[i], high = num[i];
if (mp_bound.find(num[i]+1) != mp_bound.end()) high = mp_bound[num[i]+1];
if (mp_bound.find(num[i]-1) != mp_bound.end()) low = mp_bound[num[i]-1];
mp_bound[low] = high;//同时更新上下边界,保证num[i]只需要位移一步就能找到边界
mp_bound[high] = low;
max_len = max(max_len, high-low+1);
}
}
return max_len;
}
int main()
{
int n;
//int test[] = {-4,-1,4,-5,1,-6,9,-6,0,2,2,7,0,9,-3,8,9,-2,-6,5,0,3,4,-2};
int test[] = {-6,6,-9,-7,0,3,4,-2,2,-1,9,-9,5,-3,6,1,5,-1,-2,9,-9,-4,-6,-5,6,-1,3};
while (cin >> n){
vector<int> vc;
//n = 25;
for (int i =0 ; i < n; i ++){
int a;
cin >> a;
//vc.push_back(test[i]);
vc.push_back(a);
}
cout<<longestConsecutive(vc)<<endl;
}
return 0;
}
/*
5
5 4 3 2 1
6
100 4 200 1 3 2
*/