一,并查集
并查集这种数据结构就类似一种集合的数据结构,并且在字面上的意思我们就能知道她能完成并 ,查 两个操作 。使用并查集这种操作来完成对集合的模拟,进行一些合并的操作,下面我们将进行一些模拟的操作 ,通过图来展示
这个是集合的一个初始化操作 ,并查集的初始化操作 .
集合的合并的操作,我们能知道的是在集合的合并操作中,我们使用对指针的转化进行一个合并操作,这个合并操作,导致最后生成的集合是像一颗枝干不是很茂盛的树,这就导致这个结点下寻找根的操作是相当复杂的,后面我们将对这操作进行一个优化的处理。
首先我们看一下没有优化的并查集的代码
#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 ;
}