C++ 并查集模板

并查集一般在遇到求解冗余关系,关系合并,环的数量等问题的时候使用。不需要对各数值进行输出。

注意与有向无环图问题进行区分!

有向无环图模板看这里:有向无环图讲解及模板(C++代码)_子木呀的博客-CSDN博客_c++ 有向无环图

并查集是一个很优美的数据结构,很多情况下,提炼问题后,可以用并查集进行解决,这里总结下并查集的模板及注释,可以直接用

1.并查集的使用:

//初始化并查集
int n = xx.size();
UnionFindSet dsu(n);//初始化并查集

//将两个点合并
for(int i = 0; i < n; i++){
    dsu.merge(xx[i], xx[i])
    //......
}

2.并查集模板(直接用就行,可以根据需要修改返回值出代码) 

有两处优化:(1)查询优化 (2)路径优化

//并查集实现
class UnionFindSet{
    vector F;//并查集容器
    vector rank;//秩优化(如果两个都有很多元素的根节点相遇,将根节点选为元素较少的那一个,可以节省时间)
    int n;

public:
    //并查集初始化
    UnionFindSet(int _n){
        n = _n;
        F.resize(n);
        rank.resize(n, 1);
        for(int i = 0; i < n; i++){
            F[i] = i;
        }
    }

    //并查集查询操作
    int find(int x){
        return x == F[x] ? x : F[x] = find(F[x]);//查询优化。找到x的根节点
    }

    //并查集合并操作
    bool merge(int x, int y){
        int fx = find(x), fy = find(y);
        if(fx == fy) return false;//两个节点连在同一个根节点上则直接返回 不再合并

    //因为是将节点数少的连接到节点数多的节点上,当fx下面的节点数小于fy下面的节点数时,交换fx和fy
    //即两个根节点相遇时,将新的根节点选为节点数较多的那一个,尽量减少find(x)的次数
        if(rank[fx] < rank[fy])//合并优化
            swap(fx, fy);

        F[fy] = fx;//将fy连到fx上(将节点数少的连接到节点数多的上面)
        rank[fx] += rank[fy];//将fy连到fx后 fx下面的节点数目要更新
        return true;
    }
};

结合输入的方式,不使用类。完全可以用的一个示例:

#include 
#include
#include
#include 
#include 
#include 
#include //算法头文件
#include 
#include 
#include
using namespace std;



vector F;
// 初始化
void UnionFindSet(int N)
{
	F.resize(N + 1, -1);//如果初始化是从1开始,这里就得用N+1 如果从0开始初始化 就用N(从多少开始初始化 根据输入的数据进行调整)
	for (int i = 1; i <= N; i++)
	{
		F[i] = i; // 初始化时, 将自己初始化为自己的领导
	}
}

// 查找
int find(int n)
{
	return n == F[n] ? n : find(F[n]);
}

// 合并 这里直接在主程序里调用了查找 能够省去调用合并函数的次数
void merge(int leaderX, int leaderY)
{
	if (leaderX < leaderY)
	{
		F[leaderX] = leaderY;
	}
	else
	{
		F[leaderY] = leaderX;
	}
}

// 输入数组, 每一行表示一个集合关系, 比如第一行表示3和4属于一个集合
int input[] =
{
 1, 4,
 2, 5,
 3, 6,
 4, 2,
 5, 1,
 6, 3,
};

int main()
{
	int N;
	cin >> N;
	int numberOfSets = N;
	// 初始化领导
	UnionFindSet(N);

	int n = sizeof(input) / sizeof(input[0]) / 2;//这地方根据输入的形式调整
	int j = 0;
	for (int i = 0; i < n; i++)
	{
		int u = input[j++];
		int v = input[j++];
		u = find(u);//这里直接在主程序里调用了查找 能够省去调用合并函数的次数
		v = find(v);
		if (u != v) {	//如果没关系 就合并 最后numberOfSets就是不能合并的个数 也就是有几个圈子
			merge(u, v);
			numberOfSets--;
		}
	}
	cout << numberOfSets << endl;

	return 0;
}

统计某个节点下挂了多少个节点

#include 
#include
#include
#include 
#include 
#include 
#include //算法头文件
#include 
#include 
#include
using namespace std;
vector F;
// 初始化
void UnionFindSet(int N)
{
	F.resize(N + 1, -1);//如果初始化是从1开始,这里就得用N+1 如果从0开始初始化 就用N(从多少开始初始化 根据输入的数据进行调整)
	for (int i = 1; i <= N; i++)
	{
		F[i] = i; // 初始化时, 将自己初始化为自己的领导
	}
}

// 查找
int find(int n)
{
	return n == F[n] ? n : find(F[n]);
}

int main()
{
	int N, M;
	cin >> N >> M;
	vector> input;
	vector intput1;
	int a,b;
	for (int i = 0; i < M; i++) {
		cin >> a >> b;
		intput1.push_back(a);
		intput1.push_back(b);
		input.push_back(intput1);
	}
	// 初始化领导
	UnionFindSet(N);
	for (int i = 0; i < M; i++)
	{
		int u = input[i][0];
		int v = input[i][1];
		u = find(u);//这里直接在主程序里调用了查找 能够省去调用合并函数的次数
		v = find(v);
		if (u != v) {	//如果没关系 就合并 最后numberOfSets就是不能合并的个数 也就是有几个圈子
			F[v] = u;
		}
	}
	int numberOfSets = 0;
	for (int i = 0; i < F.size(); i++) {
		if (F[i] == 1)
			numberOfSets++;
	}
	cout << numberOfSets+1 << endl;

	return 0;
}

你可能感兴趣的:(C/C++,嵌入式知识整理,c++,stl,力扣,算法,数据结构)