数据结构之并查集

并查集类型的题目在力扣上刷题的时候经常会碰到,这个知识点,如果不懂,解题可能会很费劲,但若是掌握了并查集,解此类型的题目几乎都是套模板,说这么多了,并查集究竟是来干什么的呢,就个人理解来看,它是对数据用来进行归类的,初始状态下,我们假使每个数据都是独立的,在对这个数据进行查找类别的时候,初始时都是他们自身。并查集对外提供两个API,一个是查找find,一个是合并union,查找是查找出这个元素是属于哪个门类,合并,是合并两个元素为一个门类。它对应的数据结构如下:

function DSU(arr){
    let re = {};
    for(let i=0;i<arr.length;i++){
        re[arr[i]]= arr[i];
    }
    this.find = function(k){
        if(re[k]!=k){
            re[k] = this.find(re[k]);
        }
        return re[k];
    }
    this.union = function(a,b){
        let pa = this.find(a);
        let pb = this.find(b);
        if(pa!=pb){
            re[pa] = pb;
        }
    }
}

好,我们现在看一道经典的并查集的题目,朋友圈问题

思路:这样的题目有特定的几个步骤,第一个是初始化我们的并查集,我们可以将每个学生标识为0-(N-1),然后就是将是朋友的学生进行union操作,最后在判断有几个朋友圈的时候,我们可以通过一个set来存储,最后返回set的长度即可,代码如下:

function DSU(N){
    let re =[];
    for(let i=0;i<N;i++){
        re.push(i);
    }
    this.find = function(k){
        if(re[k]!=k){
            re[k] = this.find(re[k]);
        }
        return re[k];
    }
    this.union = function(a,b){
        let pa = this.find(a);
        let pb = this.find(b);
        if(pa!=pb){
            re[pa] = pb;
        }
    }
}
function findCircleNum(M){
    let len = M.length;
    if(len ==1){
        return 1;
    }
    let k = new Set();
    let dsu = new DSU(len);
    for(let i=0;i<len;i++){
        for(let j=i+1;j<len;j++){
            if(M[i][j]==1){
                dsu.union(i,j);
            }
        }
    }
    for(let i=0;i<len;i++){
        k.add(dsu.find(i));
    }
    return k.size;
}

像这样的题,还有婴儿名字,等式方程的可能性,都是相似的解法,接下来我们再来看一个稍微进阶版的题目

这道题当然可以不用并查集去解决,我在不了解并查集的时候,解此道题的思路是用一个set去存放每个值,然后遍历每个数组中的值,当在set中没有这个数的前一个值时,那说明这个值是起点,然后往后在set里面寻找value+1,直到没找到,代码如下:

    int longestConsecutive(vector<int>& nums) {
        set<int> s;
        for(int i=0;i<nums.size();i++){
            s.insert(nums[i]);
        }
        set<int>::iterator it;
        int maxlen = 0;
        for(it = s.begin();it!= s.end();it++){
            if(!s.count(*it-1)){
                int cur = *it;
                int len = 1;
                while(s.count(cur+1)){
                    cur++;
                    len++;
                }
                maxlen = max(maxlen,len);
            }
        }
        return maxlen;
    }

今天我们也可以用并查集的思路来解决这个问题,在解决这道题的时候与上面几道题有点不同,为了方便查找到某个值连续后的最大值,我们可以在初始化,将每个值+1赋给这个值,举个例子当数组是3,4,8的时候,可以对应的map为{3:4 ,4:5, 8:9},在find的时候,如果当我们查找为3的时候,我们最后得到的值为5,当我们最后查找8的时候,最后得到的值为9。这样将最后值与当前值就可求出连续最长的数组长度了。代码如下:

var longestConsecutive = function(nums) {
    let s = {};
    let len = nums.length;
    for(let i=0;i<len;i++){
        s[nums[i]]=nums[i]+1;
    }
    function find(x){
        if(s[x]==undefined){  // 递归出界条件
            return x;
        }
        s[x] = find(s[x]);
        return s[x];
    }

    let maxLen = 0;
    for(let i=0;i<len;i++){
        let num = nums[i];
        let p = find(num);
        maxLen = Math.max(maxLen,p-num);
    }
    return maxLen;
};

其实刷题很多时候就是要多思考,对于某个知识点深入思考了,认真解决一道后,这类型的问题就不成多大问题了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值