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格式,所以对于算法的原理我就不详细的说了,大家可以自行去百度(也可以看代码),如果大家需要详细的解释代码的话,我也会在后续的更新中细细的说的。