并查集这种数据结构使用数组实现 虽然是树形结构 但是是数组实现
原始形式
每一个元素都有自己id值 如果id值一样 就是一组元素
合并时 遍历每一个元素 更改其id值
#pragma once
#include <iostream>
#include<cassert>
using namespace std;
//没个元素都有自己id值 如果id值一样 就是一组元素
//合并时 遍历没个元素 更改其id值
class UnionFind1
{
private:
int* parent;
int count;
public:
UnionFind1(int n)
{
count = n;
parent = new int[n];
for (int i = 0;i < n;i++)
{
parent[i] = i;
}
}
~UnionFind1()
{
delete[] parent;
count = 0;
}
int find(int p)
{
assert(p >= 0 && p < count);
return parent[p];
}
bool isConnected(int p, int q)
{
return find(p) == find(q);
}
void unionElements(int p, int q)
{
int pparent = find(p);
int qparent = find(q);
if(find(p)==find(q))
{
return;
}
for (int i = 0;i < count;i++)
{
if (parent[i] == pparent)
{
parent[i] = qparent;
}
}
}
};
使用类似链表的形式 把值设置成其父节点的值
#pragma once
#include <iostream>
#include<cassert>
using namespace std;
//将储存节点的父节点的值 可能会造成查找很慢
class UnionFind2
{
private:
int* parent;
int count;
public:
UnionFind2(int n)
{
parent = new int[n];
count = n;
for (int i = 0;i < n;i++)
{
parent[i] = i;
}
}
~UnionFind2()
{
count = 0;
delete[] parent;
}
int find(int p)
{
assert(p >= 0 && p < count);
while (parent[p] != p)
{
p = parent[p];
}
return p;
}
bool isConnected(int p, int q)
{
return find(p) == find(q);
}
void unionElements(int p, int q)
{
int proot = find(p);
int qroot = find(q);//尽量少调用函数 将返回值赋给变量
if (proot == qroot)return;
parent[proot] = qroot;//根节点连接另一个根节点
}
};
将子树少的节点指向子树多的节点
#pragma once
#include <iostream>
#include<cassert>
using namespace std;
class UnionFind4
{
private:
private:
int* parent;
int count;
int* sz;
public:
UnionFind4(int n)
{
parent = new int[n];
sz = new int[n];
count = n;
for (int i = 0;i < n;i++)
{
parent[i] = i;
sz[i] = 1;
}
}
~UnionFind4()
{
count = 0;
delete[] parent;
delete[] sz;
}
int find(int p)
{
assert(p >= 0 && p < count);
while (p != parent[p])
{
p = parent[p];
}
return p;
}
int isConnected(int p, int q)
{
return find(p) == find(q);
}
void unionElements(int p, int q)
{
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot)
{
return;
}
if (sz[pRoot] > sz[qRoot])
{
parent[qRoot] = pRoot;
sz[pRoot] += sz[qRoot];
}
else
{
parent[pRoot] = qRoot;
sz[qRoot] += sz[pRoot];
}
}
};
将层数少的节点指向层数多的节点 维护rank
#pragma once
#include <iostream>
#include<cassert>
using namespace std;
class UnionFind3
{
private:
int* parent;
int count;
int* rank;
public:
UnionFind3(int n)
{
parent = new int[n];
rank = new int[n];
count = n;
for (int i = 0;i < n;i++)
{
parent[i] = i;
rank[i] = 1;
}
}
~UnionFind3()
{
count = 0;
delete[] parent;
delete[] rank;
}
int find(int p)
{
assert(p >= 0 && p < count);
while (p != parent[p])
{
p = parent[p];
}
return p;
}
int isConnected(int p, int q)
{
return find(p) == find(q);
}
void unionElements(int p, int q)
{
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot)
{
return;
}
if (rank[pRoot] > rank[qRoot])
{
parent[qRoot] = pRoot;
}
else if (rank[pRoot] < rank[qRoot])
{
parent[pRoot] = qRoot;
}
else
{
parent[pRoot] = qRoot;
rank[qRoot]+=1;
}
}
};
查找的优化 链式查找
两种优化方法 第一种 将当前节点的父节点改成父亲的父亲
第二种 使子节点直接指向根节点(由于使用了递归 所以实现效率还没有第一种高 和理论想法不符)
#pragma once
#include <iostream>
#include<cassert>
using namespace std;
class UnionFind5
{
private:
int count;
int* rank;
int* parent;
public:
UnionFind5(int n)
{
parent = new int[n];
rank = new int[n];
count = n;
for (int i = 0;i < n;i++)
{
parent[i] = i;
rank[i] = 1;
}
}
~UnionFind5()
{
count = 0;
delete[] parent;
delete[] rank;
}
int find(int p)
{
assert(p >= 0 && p < count);
while (p != parent[p])
{
parent[p] = parent[parent[p]];
p = parent[p];
}
//方法2 递归调用 在find的过程中 递归地将没给元素指向根节点
while (p != parent[p])
{
parent[p] = find(parent[p]);
}
return p;
}
int isConnected(int p, int q)
{
return find(p) == find(q);
}
void unionElements(int p, int q)
{
int pRoot = find(p);
int qRoot = find(q);
if (pRoot == qRoot)
{
return;
}
if (rank[pRoot] > rank[qRoot])
{
parent[qRoot] = pRoot;
}
else if (rank[pRoot] < rank[qRoot])
{
parent[pRoot] = qRoot;
}
else
{
parent[pRoot] = qRoot;
rank[qRoot] += 1;//教程中没有提及rank的维护
}
}
};