【数据结构】 | 并查集|高级数据结构| 并查集的实现

一,并查集

并查集这种数据结构就类似一种集合的数据结构,并且在字面上的意思我们就能知道她能完成 两个操作 。使用并查集这种操作来完成对集合的模拟,进行一些合并的操作,下面我们将进行一些模拟的操作 ,通过图来展示
在这里插入图片描述

这个是集合的一个初始化操作 ,并查集的初始化操作 .
在这里插入图片描述
集合的合并的操作,我们能知道的是在集合的合并操作中,我们使用对指针的转化进行一个合并操作,这个合并操作,导致最后生成的集合是像一颗枝干不是很茂盛的树,这就导致这个结点下寻找根的操作是相当复杂的,后面我们将对这操作进行一个优化的处理。
首先我们看一下没有优化的并查集的代码


#include <iostream>
using namespace std ;
const int N = 200010 ; 
int s[N] ;
//并查集的初始化
void init () 
{
    for(int i = 1 ; i <=  n ; i ++ )
    {   
        s[i] = i ; 
    }
}
//并查集的头节点
int find_set(int x )
{
    return x == s[x] ? x : find_set(s[x]) ; 
}
//并查集合并操作
void merge_set( int x , int y )
{
    x = find_set(x) ; 
    y = find_set(y) ; 
    if(x != y )
    s[x] = s[y] ;
}


int main () 
{
    return 0 ;
}

二, 并查集的优化

首先,并查集的优化分成两个部分,第一部分就是对合并的时候的优化,第二部分就是对查询的优化,首先我们要知道查询上的优化又叫路径压缩,我们在完成查询的优化的时候一般就算完成了合并的优化。
综上我们只讲述一种路径压缩的优化的办法;
只是更改查询代码中的一部分操作;

int find () 
{ 
	if( x != s[x])  s[x] = find(s[x]);
	return s[x]; 
}

并查集完成代码

#include <iostream>
using namespace std ;


const int N = 100010 ; 

int s[N] ;

int n , m; 

void init () 
{
    for(int i = 1 ; i <= n ; i ++ )
    s[i] = i ;
}

int find_fa(int x )
{
    if(x != s[x])
    s[x] = find_fa(s[x]) ;
    return s[x] ;

}

void merge_set( )
{
    int a , b ;
    cin >> a >> b ;
    a = find_fa(a) , b = find_fa(b) ;
    s[a] = s[b] ; 
    return ;
}

void question () 
{
    int a , b ;
    cin >> a >> b ;
    if(find_fa(a) == find_fa(b) )
    cout << "Y" << endl ;
    else cout << "N" << endl ;
}
int main () 
{
    ios :: sync_with_stdio(false) ;
    cin >> n >> m ;
    init() ;
    while ( m -- )
    {
        char s ;
        cin >> s ;
        if(s == '1')
        merge_set() ;
        else 
        question() ;
    }
    return 0 ;
}

三,并查集扩展

——带权做法

因为并查集的操作,在涉及权值的只有两步操作,首先是第一步完成初始化操作的步骤,另一部就是完成集合合并的时候的操作,所以我们来进行这两个步骤的讨论

首先是初始化的操作,我们只需要再加入一个权值数组d[N] 就能完成权值的赋值操作,再对他进行赋值操作。

然后就是集合合并的操作,当集合面临合并问题的时候,我们只要把权值赋值到顶结点就行,因为我们查询的时候也是调用顶部元素

#include <iostream>
using namespace std ;
int n , m ;
const int N = 100010  ;
int s[N] ,sum[N];

void init ()
{
    for(int i = 1 ; i<= n ; i ++ )
        s[i] = i ,sum[i] = 1 ;
}

int find_sd (int x )
{
    if(x != s[x] )  s[x] =find_sd(s[x]) ;
    return s[x] ;
}
void merge_add()
{
    int a , b ;
    cin >> a >> b ;
    a = find_sd(a) , b= find_sd(b) ;
    if( a == b ) return ; 
    s[a] = s[b] ;

    sum[b] += sum[a] ;
}

void Y_N()
{
    int a , b ;
    cin >> a >> b ;
    if(find_sd(a) == find_sd(b ))
        cout << "Yes" << endl; 
    else cout << "No" << endl ;
}
void num () 
{
    int a ;
    cin >> a ;

    a = find_sd(a) ;
    cout << sum[a] << endl ;
}
int main ()
{
    ios :: sync_with_stdio(false ) ;
     cin >> n >> m ;
    init();
   
    while ( m -- )
    {
        string str ;
        cin >> str ;
        if(str == "C") 
            merge_add() ;
        else if(str == "Q1")
            Y_N() ;
        else 
        num();
    }
    return 0 ;
}
——判断集合内权值

集合内权值一般来说用来判断集合内不同的群组之间的关系使用的。来判定集合内不同群组之间的关系。

在这里插入图片描述使用队内权值的方法进行维护—— 食物链思想
例题——食物链

#include <iostream>
using namespace std ;
const int N = 1000010 ;
int n , k ;
int s[N] ,d[N] ;

int find ( int x )
{
    if(s[x] != x ) {int t = find(s[x]);
     d[x] += d[s[x]] ;
     s[x] = t ;}
    return s[x] ;
}


int main ()
{
   cin >> n >> k ;
   for(int i = 1 ; i <= n ; i ++ )
   {
        s[i] = i ;
   }    int ans = 0 ;
   while ( k -- )
   {
    int t , a , b ;
    cin >> t >> a >> b ;
    if(a > n || b > n ) ans ++ ;
    else if(t == 1)
    { 
        int pa = find(a) , pb = find(b) ;
        if( pa == pb && (d[a] - d[b]) % 3)
            ans ++  ;
        else if(pa != pb)
        {
            s[pa] =pb ; 
            d[pa] = (d[b] - d[a]) ;
        }

    }
    else
    {
        int pa = find(a) , pb = find(b) ;
        if( pa == pb && (d[a] - d[b] - 2) % 3)
            ans ++ ;
        else if( pa != pb )
        {
            s[pa] = pb ;
            d[pa] = (d[b] - d[a]  - 1) ;
        }
    }
   }
   cout << ans << endl ;

    return 0 ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值