128. Longest Consecutive Sequence
Given an unsorted array of integers, find the length of the longest consecutive elements sequence.
Your algorithm should run in O(n) complexity.
Example:
Input: [100, 4, 200, 1, 3, 2]
Output: 4
Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.
是否可能出现重复数字?
方法0: sort
Time complexity: O(nlogn)
Space complexity: O(n)
不符合O(n)的要求。
方法1: hash (offline)
九章: https://www.jiuzhang.com/solutions/longest-consecutive-sequence/#tag-highlight-lang-cpp
grandyang:http://www.cnblogs.com/grandyang/p/4276225.html
思路:
题目要求O(n)时间强烈暗示了hash,那么用什么值来做key?一开始以为应该是起点或者终点,但是这样并不方便同时做两个端点的查找,除非做sort,那么时间复杂度就会超出。上面这个解的做法是以每一个点为key,映射中存储的是这个点所在interval的长度。不过既然求解的是interval的最大长度,其实只要降维到一个max_length 就可以了。遍历数组,每次以一个点为初始,发起上下的interval expansion,记录最大lenght。这里提速的关键是每次扩张过的up和down,就把对应的key从hash中erase。这样做是因为:1. 避免重复遍历相同的interval多次:如果存在[1, 2, 3, 4] 以1 发起的计数会遍历整个interval,不删除的话,以2,3,4发起的会重复整个过程,复杂度会变成O(n^2);2. 这个做法限于offline,也就是提前知道了所有已存在的数字,如果是online就没办法计算因为相当于丢失了一个interval起点和终点的信息,有新进入的元素无法更新长度。如果要求online的版本可以使用方法2。
易错点
- 初始化 cur = 1: 只要set里面有数字,result不会小于1
- 不要用set:STL中set是用红黑树实现的,建树会O(nlogn)
Complexity
Time complexity: O(n), 由于每次消除了up和down,同一个数不会被访问两次
Space complexity: O(n)
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> hash(nums.begin(), nums.end());
int result = 0;
for (auto c: hash){
int cur = 1;
int up = c + 1;
int down = c - 1;
while (hash.find(up) != hash.end()){
hash.erase(hash.find(up));
cur++;
up ++;
}
while (hash.find(down) != hash.end()){
hash.erase(hash.find(down));
cur++;
down--;
}
result = max(result, cur);
}
return result;
}
};
方法2: hash (online)
花花酱: https://www.youtube.com/watch?v=rc2QdQ7U78I
思路:
如果是online的话,就必须维持两个端点的信息,以准备随时和新来的点融合or被新来的点和另外的interval融合。上面这个做法没有直接记录端点,而是用端点作为key来记录长度。并且对每一个数进行find判断,可以排除重复input,这样就只有在interval之外的点会访问到这个interval,而相关的信息只有interval的两个端点,中间的key是否update也就不重要了。
对每个数字进行三种情况的分类:
- 上下都没有数字,那么它自己建立key:1
- 上或下有数字,那么就把上或下的端点所对应的长度取过来+1,并且改写另外一个端点对应的长度。这一点由于确保遇到的数字一定是端点并且携带着自己interval长度的信息,另一个端点用key + value 或者key - value就找到了
- 上和下都有数字,那么就要把两边端点的长度都改为l + r + 1
易错点
- 要防止重复查找:grandyang 的解释 http://www.cnblogs.com/grandyang/p/4276225.html,比如数组 [1, 2, 0, 1],当0执行完以后,HashMap中的映射为 {1->2, 2->3, 0->3},可以看出此时0和2的映射值都已经为3了,那么如果最后一个1还按照原来的方法处理,随后得到结果就是7,明显不合题意。还有就是,之前说的,为了避免访问不存在的映射值时,自动创建映射,我们使用m.count() 先来检测一下,只有存在映射,我们才从中取值,否则就直接赋值为0。
2
Complexity
Time complexity: O(n)
Space complexity: O(n)
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_map<int, int> h;
int ans = 0;
for (int num : nums) {
if (h.count(num)) continue;
auto it_l = h.find(num - 1);
auto it_r = h.find(num + 1);
int l = it_l != h.end() ? it_l->second : 0;
int r = it_r != h.end() ? it_r->second : 0;
int t = l + r + 1;
h[num] = h[num - l] = h[num + r] = t;
ans = max(ans, t);
}
return ans;
}
};