并查集可支持查找元素所属的集合以及不同集合元素的合并。
并查集有3个过程:
构建集合
查找指定元素的所属集合(用该集合的代表元素表示,如果是树,就是树根)
不同集合的合并
C++代码如下:
1. DisjointSet.h
//
// Created by ZhuJiahui on 2016/8/10.
//
#ifndef DISJOINTSET_DISJOINTSET_H
#define DISJOINTSET_DISJOINTSET_H
#include <vector>
using namespace std;
class DisjointSet
{
public:
DisjointSet(std::vector<int> numbers);
int FindSet(int node);
void Union(int nodeA, int nodeB);
public:
std::vector<int> data;
std::vector<int> fathers;
std::vector<int> ranks;
};
#endif //DISJOINTSET_DISJOINTSET_H
2. DisjointSet.cpp
//
// Created by ZhuJiahui on 2016/8/10.
//
#include "DisjointSet.h"
// 构造函数初始化(MAKE_SET)
DisjointSet::DisjointSet(vector<int> numbers)
{
for (int i = 0; i < numbers.size(); ++i)
{
data.push_back(numbers[i]); // 复制数据
fathers.push_back(i); // 父节点为自身
ranks.push_back(0); // 深度为0
}
}
// 查找节点的父节点[编号0到data.size()-1]
int DisjointSet::FindSet(int node)
{
// 当节点的父节点不是自身时递归查找
/*
if (node != fathers[node])
{
return FindSet(fathers[node]);
}
else
{
return node;
}*/
// 路径压缩(比递归查找更快)
if (node != fathers[node])
{
fathers[node] = FindSet(fathers[node]);
}
return fathers[node];
}
// 集合合并(按秩合并)
void DisjointSet::Union(int nodeA, int nodeB)
{
int numberA = this->FindSet(nodeA);
int numberB = this->FindSet(nodeB);
if (ranks[numberA] > ranks[numberB])
{
fathers[numberB] = numberA; // A作为B的父节点
}
else
{
fathers[numberA] = numberB; // B作为A的父节点
if (ranks[numberA] == ranks[numberB])
{
++ranks[numberB];
}
}
}
3. main.cpp
#include <iostream>
#include <vector>
#include "DisjointSet.h"
using namespace std;
int main(int argc, char *argv[])
{
vector<int> numbers = {11, 22, 33, 44, 55, 66, 77, 88, 99, 111};
DisjointSet ds(numbers);
ds.Union(1, 3);
ds.Union(4, 6);
ds.Union(0, 2);
ds.Union(7, 8);
ds.Union(0, 1);
ds.Union(4, 5);
ds.Union(1, 2);
// 得到每个元素所属的集合类别
for (size_t i = 0; i < ds.fathers.size(); ++i)
{
cout << ds.FindSet(i) << " ";
}
return 0;
}
编译环境(CLion 2016 with MinGW G++, GDB 7.11)
输出结果:
3 3 3 3 6 6 6 8 8 9
也就是说前4个元素在一个集合中,中间3个元素在一个集合中,倒数第2和第3个元素在一个集合中,最后一个元素单独成一个集合。