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。

易错点

  1. 初始化 cur = 1: 只要set里面有数字,result不会小于1
  2. 不要用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也就不重要了。

对每个数字进行三种情况的分类:

  1. 上下都没有数字,那么它自己建立key:1
  2. 上或下有数字,那么就把上或下的端点所对应的长度取过来+1,并且改写另外一个端点对应的长度。这一点由于确保遇到的数字一定是端点并且携带着自己interval长度的信息,另一个端点用key + value 或者key - value就找到了
  3. 上和下都有数字,那么就要把两边端点的长度都改为l + r + 1

易错点

  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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值