【算法与数据结构】——并查集

【算法与数据结构】——并查集,第1张

并查集
【算法与数据结构】——并查集
【算法与数据结构】—— 并查集
两位大佬写的太好了,现在加入了一些自己的理解,进行简化。


侵删。


void init(int n)     				//并查集的初始化
{
    for(int i = 0; i < n; i++){
        pre[i] = i;     			//每个结点的上级都是自己 
    } 
}
int find(int x)					//查找x的代表元
{
	//找这个树的根节点(即"教主"),根节点(即"教主")的特点是:"他的上级是他自己"!
	while(pre[x] != x)			//如果x的前驱结点不是自己,就一直找,目的是:找到根节点(即"教主")
		x = pre[x];
	return x;               //找到"教主"后,返回"教主"
}
void join(int x,int y)    // 将两个集合合并                
{
    int fx=find(x), fy=find(y); 
    if(fx != fy) 
        pre[fx]=fy;	//fy做fx的前驱结点
}

find()函数优化:优化后的特点是 : =>将 可能的 “单支树结构(一字长蛇形)” 修改为 “N叉树”,
即再次遍历时,不用再遍历那么多,只需要遍历一次,即可遍历到他的最终大BOSS(也叫教主,也叫根节点)
即:第一次执行查找 *** 作的时候是实现没有压缩效果的,只有在之后才有效。


void init(int n)     				//并查集的初始化
{
    for(int i = 0; i < n; i++){
        pre[i] = i;     			//每个结点的上级都是自己 
    } 
}
int find(int x)     				//查找结点 x的根结点 
{
    if(pre[x] == x) return x;		//递归出口:x的上级为 x本身,即 x为根结点        
    return pre[x] = find(pre[x]);	//此代码相当于先找到根结点 rootx,然后pre[x]=rootx 
}
void join(int x,int y)    // 将两个集合合并                
{
    int fx=find(x), fy=find(y); 
    if(fx != fy) 
        pre[fx]=fy;	//fy做fx的前驱结点
}

用一道力扣的题来巩固入门
547. 省份数量
题解

模板
先默写初始化init()函数,find()函数,join()函数;
然后再主要实现功能的函数findCircleNum()中,
先初始化 比如说: 1->1 2->2 3->3…;
再合并 比如说: 1->2->3 2->4 5->5 6->6…;
再用find()函数,将一字长蛇阵,转成N叉树; 1->2,1->3,1->4,5->5,6->6 (可以理解为:老板->员工)
最后用set去重;(最终pre中放的都是老板,用set去重,得到有几个老板,也就是说几个省份)

class Solution {
public:
    int pre[200];//放省份,最多为y个
    void init(int n)     				//并查集的初始化
    {
        for(int i = 0; i < n; i++){
            pre[i] = i;     			//每个结点的上级都是自己 
        } 
    }
    int find(int x)     				//查找结点 x的根结点 
    {
        if(pre[x] == x) return x;		//递归出口:x的上级为 x本身,即 x为根结点        
        return pre[x] = find(pre[x]);	//此代码相当于先找到根结点 rootx,然后pre[x]=rootx 
    }
    void join(int x,int y)    // 将两个集合合并                
    {
        int fx=find(x), fy=find(y); 
        if(fx != fy) 
            pre[fx]=fy;	//fy做fx的前驱结点
    }

    
    int findCircleNum(vector<vector<int>>& isConnected) {
        int cnt = 0;
        int y = isConnected.size();
        int x = isConnected[0].size();
        init(y);//初始化

        for(int i = 0 ; i< y;i++){//合并
            for(int j = 0 ; j< x ;j++){
                if(isConnected[i][j]==1){
                     join(i,j);//如果是同一省份,就合并
                }
            }
        }
        for(int i = 0 ; i< y;i++){//再将一字长蛇阵变成N叉树,
        //即,每个人的上级要么是一个人(最终大BOSS),要么是自己
                find(i);
        }
        unordered_set<int>s;
        for(int i = 0;i<y;i++){//一个老板对应多个员工,将老板去重
            cout<<pre[i]<<" ";
            s.insert(pre[i]);
        }
        return s.size();//最终返回有几个老板
    }
};

再来一个题吧!!!
128. 最长连续序列

class Solution {
public:

	map<int,int>pre;
	void init(int n,vector<int>& v)     				//并查集的初始化
	{
    	for(int i = 0; i < n; i++){
       	 pre[v[i]] = v[i];     			//每个结点的上级都是自己 
    	} 
	}
	int find(int x)					//查找x的代表元
	{
		//找这个树的根节点(即"教主"),根节点(即"教主")的特点是:"他的上级是他自己"!
		while(pre[x] != x)			//如果x的前驱结点不是自己,就一直找,目的是:找到根节点(即"教主")
			x = pre[x];
		return x;               //找到"教主"后,返回"教主"
	}
	void join(int x,int y)    // 将两个集合合并                
	{
  	  int fx=find(x), fy=find(y); 
   	 	if(fx != fy) 
    	    pre[fx]=fy;	//fy做fx的前驱结点
	}

    int longestConsecutive(vector<int>& nums) {
        int n =nums.size();
        if(n==0)return 0;
        if(n==1)return 1;

        //接下来,nums至少有两个数
        set<int>s;//对nums去重,再顺便对set进行排序
        for(int i = 0;i<n;i++){
            s.insert(nums[i]);
        }
        vector<int>v;
        for(auto it=s.begin();it!=s.end();it++){
            v.push_back(*it);
        }
        int n1=v.size();
        cout<<"n"<<n<<";n1:"<<n1<<endl;
        init(n1,v);
        //调试代码
        /*for(int i = 0 ; i< n1;i++){/
                cout<"<
        for(int i = 0;i<n1-1;i++){
            if(v[i+1]==v[i]+1){//后边的数比前面的数大1
                join(v[i+1],v[i]);
            }
        }
        //调试代码
        /*for(int i = 0 ; i< n1;i++){/
                cout<"<
				
        for(int i = 0 ; i< n1;i++){//再将一字长蛇阵变成N叉树,
        //即,每个人的上级要么是一个人(最终大BOSS),要么是自己
                find(v[i]);
        }
				
			  //调试代码
        /*for(int i = 0 ; i< n1;i++){//再将一字长蛇阵变成N叉树,
        //即,每个人的上级要么是一个人(最终大BOSS),要么是自己
                cout<

        //判断好是统计员工还是老板,如果只统计老板,那最后就用set去重,
        //如果统计的是:每家公司中,员工和老板人数的总和,那就不用set,直接统计重复数字的个数就行
        int max=0;
        map<int,int>cnt;
        for(int i = 0 ; i< n1;i++){//再将一字长蛇阵变成N叉树,
        //即,每个人的上级要么是一个人(最终大BOSS),要么是自己    
                cnt[pre[v[i]]]++;
                if(cnt[pre[v[i]]]>max)max=cnt[pre[v[i]]];
        }

        return max;
    }
};

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/langs/568283.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-09
下一篇 2022-04-09

发表评论

登录后才能评论

评论列表(0条)

保存