⭐LeetCode 128. Longest Consecutive Sequence

题目描述

在这里插入图片描述

知识点

并查集

结果

在这里插入图片描述
不太好,排名好低呀

实现

码前思考

  1. 要不是实现知道这个题得用并查集,我可能根本想不到要用并查集;
  2. 时间复杂度为 O ( n ) O(n) 这就很恐怖,不能使用动态规划里的LIS了;
  3. 考虑到数字重复的情况,通过题意可以知道,数字重复和一个数字,它们的价值是等价的,也就是说重复数字可以看作是一个数字!!!
  4. 由于题中没有给出数字的大小,可能是正的,也可能是负的,我们得hash到可以到可用的非负数范围之内,但是由于我不太会hash,所以失败了。最后思考发现可以使用unordered_map来实现,这种数据结构采用哈希表实现,时间复杂度为 O ( 1 ) O(1) ,而map使用红黑树,时间复杂度为 O ( l o g N ) O(logN)
  5. 我这里的并查集father的意思是:以该节点为起始的最长序列包含的元素。 但是是不包含该节点的。这个思路有点绕弯,后面的码后反思的那个思路更好一点;
  6. 由于我们是每次遍历读取nums中的数据curcur合并到前一个数字cur-1上,所以可能前一个数字cur-1不存在于nums中,所以还得另外把每个数字的前一个数字cur-1加到unordered_map中,这样确实复杂多了,唉。

代码实现

//时间复杂度为O(n)这就很恐怖,不能使用动态规划了
class Solution {
private:
    //father数组,初始化为自己
    //使用unordered map来存储
    unordered_map<int,int> father;

    //每棵树的结点的个数,表示从该结点开始的最小序列的大小
    unordered_map<int,int> count;

    //最大连通分量的大小
    int ans;
public:
    int longestConsecutive(vector<int>& nums) {
        if(nums.size() == 0){
            return 0;
        }


        //初始化两个数组
        //初始化father
        for(auto i : nums){
            father[i] = i;
            father[i-1] = i-1;
            count[i] = 1;
            count[i-1] = 1;            
        }
        

        ans = 1;

        for(auto i : nums){
            
            //对cur进行映射,cur应该是从1开始的
            int cur = i;
            int pre = i-1;
            
            int fatherCur = findFather(cur);
            int fatherPre = findFather(pre);

            if(fatherCur == fatherPre){//说明已经在一个并查集里面了,不需要进行操作
                continue;
            }else{  //如果不在一个并查集里面,需要进行合并
                //将大的数合并到小的数的集里面
                Union(fatherCur,fatherPre);
                //表示数量也进行了合并
                count[fatherPre] = count[fatherPre] + count[fatherCur];
                
                if(count[fatherPre] > ans){
                    ans = count[fatherPre];
                }
            }

        }

        return ans-1;
    }

    int findFather(int x){
        if(father[x] == x){
            return x;
        }else{
            int tmp = findFather(father[x]);
            father[x] = tmp;
            return tmp;
        }
    }

    void Union(int cur,int pre){
        father[cur] = pre;
    }
};

码后反思

  1. 网友的并查集的思想比我的更好:
    在这里插入图片描述
    他是以当前结点作为基准,去寻找后继结点的。而我是以当前结点作为基准,去寻找前驱结点。然而我们的并查集的概念是一致的,最终导致我的思路要绕一些弯。
  2. 修改思路后的代码:
    在这里插入图片描述
    //时间复杂度为O(n)这就很恐怖,不能使用动态规划了
    class Solution {
    private:
        //father数组,初始化为自己
        //使用unordered map来存储
        unordered_map<int,int> father;
    
        //每棵树的结点的个数,表示从该结点开始的最小序列的大小
        unordered_map<int,int> count;
    
        //最大连通分量的大小
        int ans;
    public:
        int longestConsecutive(vector<int>& nums) {
            if(nums.size() == 0){
                return 0;
            }
    
    
            //初始化两个数组
            //初始化father
            for(auto i : nums){
                father[i] = i;
                count[i] = 1;        
            }
            
    
            ans = 1;
    
            for(auto i : nums){
                
                //对cur进行映射,cur应该是从1开始的
                int cur = i;
                int next = i+1;
                
                int fatherCur = findFather(cur);
    
                if(father.count(next) == 0){
                    continue;
                }else{
                    int fatherNext = findFather(next);
                    if(fatherCur == fatherNext){//说明已经在一个并查集里面了,不需要进行操作,考虑了重复数字的情况
                        continue;
                    }else{  //如果不在一个并查集里面,需要进行合并
                        //将小的数合并到大的数的集里面                    
                        Union(fatherCur,fatherNext);
                        //表示数量也进行了合并
                        count[fatherCur] = count[fatherNext] + count[fatherCur];
                        
                        if(count[fatherCur] > ans){
                            ans = count[fatherCur];
                        }
                    }
                }
    
    
    
            }
    
            return ans;
        }
    
        int findFather(int x){
            if(father[x] == x){
                return x;
            }else{
                int tmp = findFather(father[x]);
                father[x] = tmp;
                return tmp;
            }
        }
    
        void Union(int cur,int next){
            father[next] = cur;
        }
    };
    
  3. 需要注意,判断unordered_map里面有没有一个key得使用count(),不能用key对应的mp[key]==0来判断,原因很显然,不要投机取巧!
  4. 并查集的方法其实有点蠢,使用C++ STL的unordered_map自带的hash功能,就能很快的实现这个功能。看来自己是被并查集(或者是自己的思想)束缚住了,显然并查集是平白地在hash的基础上增加了hash的工作量!
  5. 这道题最大的收获应该是掌握了hash的手法unordered_map,受教了!
发布了138 篇原创文章 · 获赞 3 · 访问量 3782

猜你喜欢

转载自blog.csdn.net/yc_cy1999/article/details/105354117