Algorithm第四版算法 C++实现(一)——union-find算法(并查集)

union-find是用来解决动态连通性问题的算法,可以返回对应的图的连通分量数
这是书中给出的对应API
请添加图片描述

qucik-find

main.cpp

#include <iostream>
#include "head.h"
using namespace std;
int main()
{
	int n;
	cin >> n;
	union_find uf(n);
	uf.uni();
	return 0;
}

/*
test data
10
4 3
3 8
6 5
9 4 
2 1
5 0
7 2
6 1
1 0
6 7
*/

head.h

#pragma once
#include<vector>
/*Union-find 算法*/
class union_find
{
public:
	union_find(int n);//动态连通性
	void uni();//动态连通性
	void uf(int p, int q);
	bool connected(int p, int q);
private:
	int count=0;
	int n = 0;
	std::vector<int> a;
	std::vector<int> size;
	int find(int p);
};

head.cpp

#pragma once
#include"head.h"
#include<iostream>
union_find::union_find(int num)
{
	n = num;
	count = num;
	a.resize(num);
	for (int i = 0;i<n; i++)
	{
		a[i] = i;
	}
}
bool union_find::connected(int p, int q)
{
	if (a[p] == a[q])
	{
		return true;
	}
	return false;
}
int union_find::find(int p)
{
	return a[p];
}
void union_find::uf(int p,int q)
{
	int pv = find(p);
	int qv = find(q);
	if (pv == qv)
	{
		//std::cout << "未减\n";
		return;
	}
	for (int i = 0; i < n; i++)
	{
		if (pv == a[i])
		{
			a[i] = qv;
		}
	}
	count--;
	for (int j = 0; j < n; j++)
	{
		std::cout << a[j] << ' ';
	}
	std::cout << '\n';
}
void union_find::uni()
{
	int p, q;
	std::cout << "请输入测试用例" << std::endl;
	for (int i = 0; i < n; i++)
	{
		std::cin >> p >> q;
		if (connected(p, q))
		{
			continue;
		}
		uf(p, q);
	}
	std::cout << count << " components";
}

运行结果
在这里插入图片描述

quick-find中,find函数是很快的,但是quick-find对于每一次输入,union都需要扫描整个数组。所以quick-find一般无法解决大型问题。
为此,我们提供了quick-union算法。

quick-union算法

//只需要修改find与union两个函数便可
int union_find::find(int p)
{
	while (p != a[p])
	{
		p = a[p];
	}
	return p;
}
void union_find::uf(int p,int q)
{
	int proot = find(p);
	int qroot = find(q);
	if (proot == qroot)
	{
		return;
	}
	a[proot] = qroot;
	count--;

	for (int j = 0; j < n; j++)
	{
		std::cout << a[j] << ' ';
	}
	std::cout << '\n';
}

运行结果:
在这里插入图片描述
但是quick-union在最坏的情况下(即每个新节点都链接在原来的树上方),其时间复杂度围是 平方级别 的。也就是说,可能会将大树链接在小树上,不断增加树的树高,使得遍历花费的时间越来越长。幸好我们只需要简单的修改就可以避免这种糟糕的情况

加权quick-union算法

head.h

#pragma once
#include<vector>
/*Union-find 算法*/
class union_find
{
public:
	union_find(int n);//动态连通性
	void uni();//动态连通性
private:
	int count=0;
	int n = 0;
	std::vector<int> a;
	std::vector<int> size;
	void uf(int p,int q);
	int find(int p);
	bool connected(int p, int q);
};

head.cpp

#pragma once
#include"head.h"
#include<iostream>
union_find::union_find(int num)
{
	n = num;
	count = num;
	a.resize(num);
	size.resize(num);
	for (int i = 0;i<n; i++)
	{
		a[i] = i;
	}
	for (int i = 0; i < n; i++)
	{
		size[i] = 1;
	}
}
bool union_find::connected(int p, int q)
{
	if (a[p] == a[q])
	{
		return true;
	}
	return false;
}
int union_find::find(int p)
{
	while (p != a[p])
	{
		p = a[p];
	}
	return p;
	//************************quick-find算法**************************
	/*return a[p];*/
}
void union_find::uf(int p,int q)
{
	int proot = find(p);
	int qroot = find(q);
	if (proot == qroot)
	{
		return;
	}
	if (size[proot] < size[qroot])
	{
		a[proot] = qroot;
		size[qroot] += size[proot];
	}
	else
	{
		a[qroot] = proot;
		size[proot] += size[qroot];
	}
	count--;
	//************************quick-union算法**************************
	/*a[proot] = qroot;
	count--;*/

	//************************quick-find算法**************************
	/*int pv = find(p);
	int qv = find(q);
	if (pv == qv)
	{
		//std::cout << "未减\n";
		return;
	}
	for (int i = 0; i < n; i++)
	{
		if (pv == a[i])
		{
			a[i] = qv;
		}
	}
	count--;*/
	for (int j = 0; j < n; j++)
	{
		std::cout << a[j] << ' ';
	}
	std::cout << '\n';
}
void union_find::uni()
{
	int p, q;
	std::cout << "请输入测试用例" << std::endl;
	for (int i = 0; i < n; i++)
	{
		std::cin >> p >> q;
		if (connected(p, q))
		{
			continue;
		}
		uf(p, q);
	}
	std::cout << count << " components";
}

在这里插入图片描述

因为我只是将书中的代码改写为cpp格式,所以对于算法的原理我就不详细的说了,大家可以自行去百度(也可以看代码),如果大家需要详细的解释代码的话,我也会在后续的更新中细细的说的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值