【C/C++】並查集


並查集操作的簡單實現

  • 原理:定義一個數組s[i]來表示第i個元素屬於哪個集團,因此初始化時s[i] = i;即每個元素都還是分散的。對於可以合並的兩個元素x與y,查找到他們兩個所屬的集團,將其中一個合並到另一個即可;
  • 代碼實現:
#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e4 + 10;
int s[maxn];

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

int find_set(int x)//查詢x的集團編號
{
	return s[x] == x?x:find_set(s[x]);
}

void union_set(int x, int y)
{
	x = find_set(x);
	y = find_set(y);
	if(x != y) s[x] = s[y];
}

int main()
{
	int n,m,x,y;
	while(~scanf("%d %d",&n,&m))
	{
		init_set(n);//初始化
		for(int i=0; i<m; i++)
		{
			scanf("%d %d",&x,&y);
			union_set(x, y);//將x的集團與y的集團合並
		}
		int ans = 0;
		for(int i=1; i<=n; i++)//統計有多少個集團
			if(s[i] == i)
				ans++;
		printf("%d\n",ans);
	}
	return 0;
}

合並的優化

  • 將兩個集團合並時,可以看做是兩個樹的合並,而高度較小的樹合並到較大的樹上可以使樹的高度不變。引入一個數組height[i]來表示樹的高度即可
  • 代碼實現:

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

void union_set(int x, int y)
{
    x = Find_set(x);
    y = Find_set(y);
    if(height[x] == height[y])
    {
        height[x]++;
        s[y] = x;
    }
    else
    {
        if(height[x] > height[y]) 
            s[y] = x;
        else 
            s[x] = y;
    }
}

查詢的優化

  • 每次搜索的過程中,如果順便將i所屬的集團改為根節點,再次查找i的時候就可以直接返回結果了
  • 代碼實現
//遞歸查詢,容易爆棧
int find_set(int x)
{
    if(x != s[x]) 
        s[x] = find_set(s[x]);
    return s[x];
}

//非遞歸查詢
int Find_set(int x)
{
    int r = x;
    while(r != s[r]) r = s[r];
    int i=x,j;
    while(i != r)
    {
        j = s[i];
        s[i] = r;
        i = j;
    }
    return r;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM