题意:给出一个无序的整型数组,求出最长的连续整数序列,例如[100, 4, 200, 1, 3, 2],的最长连续序列为[1,2,3,4],返回序列长度4即可
分析:这个题目如果数组是有序的,则一次遍历就可以得到最长的连续整数长度,但是,题目给出的是无序的序列,因此,第一个思路就有了
思路1:先对数组进行排序(增序或者减序均可),然后遍历数组,如果相邻两个元素的差值为1(增序)或者-1(减序),则在求出的序列长度上+1即可。
这个时候,忽然看到,题目要求,时间复杂度为O(N),思路1就不符合要求了,因为排序的复杂度,最少也是O(NlgN),是不满足要求的,因此,不能花时间对序列进行排序。
排除掉思路1,就开始思考不排序的情况下,如何判断数组中的数字的连续性。所谓连续,也就是这个数字+1或者-1,例如跟1连续的只有0与2,那要判断连续,就只需要考虑0与2是否在当前的数组里即可。于是就有了思路2.
思路2:遍历数组,每一个值val,判断val+1与val-1是否在数组里,但是,查找的时间复杂度,也至少是O(N),这样也是不行的,那就思考,如何把查找的复杂度降低到常量级别,想了很久都没结果,最后,参考了hash的思想,用空间来换取时间,将数组中的数散列到hash表中,即可以常量O(1)的时间复杂度来判断数值的存在与否。
思路2可以满足N的时间复杂度,但是,hash要自己写,其实也挺麻烦的,研究了stl,才发现有现成的数据结构unordered_set与unordered_map,这两个数据结构均是以hash方式来做存储与检索,我们这个题目中不需要保存映射关系,因此选用unordered_set作为hash的载体。
同时还有一个细节,就是检索的时候,是两个方向的,递增与递减,刚开始,我只向一个方向检索,hash表一直不变,导致许多重复的搜索,提交代码时,有超时的错误,后面改进为向两个方向同时检索,并且,检索到一个数值之后,将该数值从hash表中清除,最终才AC通过。
代码:
class Solution {
public:
int longestConsecutive(vector<int> &num) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
unordered_set<int> hashlist = unordered_set<int>();
for (int i = 0; i < num.size(); ++ i)
{
hashlist.insert(num[i]);
}
int maxCon = 0;
for (int i = 0; i < num.size(); ++i)
{
int sub = num[i]-1;
int tmpLen = 1;
while (hashlist.find(sub) != hashlist.end())
{
tmpLen ++;
hashlist.erase(sub);
sub--;
}
int add = num[i]+1;
while(hashlist.find(add) != hashlist.end())
{
tmpLen ++;
hashlist.erase(add);
add ++;
}
if (tmpLen > maxCon)
{
maxCon = tmpLen;
}
}
return maxCon;
}
};