【哈希表&并查集】最长连续序列

在这里插入图片描述

哈希表

先用哈希表去重,遍历哈希表中的元素num,如果num - 1不在哈希表中,说明num可以作为连续序列的左边界,然后不断查看num + 1,num + 2,… 是否在哈希表中,知道找到右边界,更新以num为左边界的连续序列的长度

若num - 1在哈希表中,说明无需再将当前num作为左边界,继续查找连续连续序列了

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> st;
        for(int num : nums) st.insert(num);
        int ans = 0;
        for(int num : st){
            if(st.find(num - 1) == st.end()){
                // num - 1不存在于st中,num才能作为左边界
                int r = num + 1;
                while(st.find(r) != st.end()) r++;
                ans = max(ans, r - num);
            }
        }
        return ans;
    }
};

并查集

构造一个连续序列的右边界作为根节点的并查集

遍历所有元素num,如果num+1存在于并查集中,连接num加入到num+1所在的连通分量,并将num+1所在集合的根节点作为父节点

重新遍历一遍所有元素num,通过find函数找到num所在分量的根结点,也就是最远右边界,从而求得连续区间的长度

class Solution {
public:
    unordered_map<int, int> parent;

    int find_root(int x){
        // x不在并查集中
        if(parent.find(x) == parent.end()) return INT_MIN;
        if(x == parent[x]) return x;
        parent[x] = find_root(parent[x]);  // 路径压缩
        return parent[x];
    }

    void merge(int x, int y){
        x = find_root(x);
        y = find_root(y);
        if(x != y) parent[x] = y;
    }

    int longestConsecutive(vector<int>& nums) {
        // 初始化并查集,自己的父节点就是自己
        for(int num : nums) parent[num] = num;
        for(int num : nums){
            if(parent.find(num + 1) != parent.end()){
                // num + 1存在于并查集中,连接num和num + 1所在的连通分量
                merge(num, num + 1);
            }
        }
        int ans = 0;
        for(int num : nums){
            ans = max(ans, find_root(num) - num + 1);
        }
        return ans;
    }
};

封装并查集并添加记录连通分量的count

class UnionFind{
    public:
        UnionFind(const vector<int>& nums){
            // 初始化并查集,自己的父节点就是自己
            for(int num : nums) {
                parent[num] = num;
                count[num] = 1;
            }
        }

        bool inline is_exist(int x){
            return parent.find(x) != parent.end();
        }
        
        int find_root(int x){
            // x不在并查集中
            if(!is_exist(x)) return INT_MIN;
            if(x == parent[x]) return x;
            parent[x] = find_root(parent[x]);  // 路径压缩
            return parent[x];
        }

        // y比x大,y作为父节点,并查集建立完成后,根节点一定是一个连续序列的右边界
        void merge(int x, int y) {
            x = find_root(x);
            y = find_root(y);

            if (x != y) {
                // x和y不属于同一连通分量,合并后再更新count
                parent[x] = y;
                int cnt = count[x] + count[y];
                count[x] = cnt;
                count[y] = cnt;
            }
        }

        // 获取x所在连通分量的节点个数
        int get_cnt(int x){
            return count[x];
        }

    private:
        unordered_map<int, int> parent; // 并查集
        unordered_map<int, int> count;  // 记录节点所在连通分量的节点个数
};

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        if(nums.size() < 2) return nums.size();

        UnionFind uf(nums);
        int ans = 1;
        for(int num : nums){
            if(uf.is_exist(num + 1)){
                // num + 1存在于并查集中,连接num和num + 1所在的连通分量
                // 这样可以将连续的数字合入一个连通分量,改成num - 1也可以
                uf.merge(num, num + 1);
                ans = max(ans, uf.get_cnt(num));
            }
        }

        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bugcoder-9905

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值