两个不相交集合是指S1 ∩ S2 == {}空集,一般包含两个操作,find和union操作
find:返回包含给定元素的集合
union把两个集合合并成一个集合
不相交集合的数组表示法,
第一个解法是用一个数组存储一int值,
如果结点为根,则为树的高度的负值-1,(因为初始为-1);如果为非根结点,则存储父结点下标
第二个解法思路也是一样,只不过数组存储的是一个结点结构,包括data值,包括p父节点,rank结点的秩,代表x的高度(从x到某一后代结点的最长简单路径上边的数目)的上界。
MakeSet:创建一个单元素集合,并且初试时秩为0
FindSet:不改变任何秩,也是采用数据压缩的解法,这种解法使查找路径中的每个结点直接指向根。
UininSet:如果根没有相同的秩,则让较大秩的根成为较小秩的根的父节点,秩保持不变
如果根有相同的秩,则任意选择两个根中的一个作为父节点,并使它的秩加1
第一种:
#include <iostream>
#include <vector>
using namespace std;
class DisjSets
{
public:
explicit DisjSets (int numElements) : s(numElements)
{
for (int i = 0; i < s.size(); i++)
s[i] = -1;
}
int find(int x) const
{
if (s[x] < 0)
return x;
else
return find(s[x]);
}
/************************************************************************/
/*返回包含x的集合,如果是根结点,返回本身,如果存在父节点,返回父节点
使用路径压缩的find,效果是从x到根的路径上的每一个
结点使它的父节点变成根,即使得路径上的每个s[x]等于
find(s[x])找到的父结点,实现了路径压缩(使路径上的每个结点的路径减少了)*/
/************************************************************************/
int find(int x)
{
if (s[x] < 0)
return x;
else
return s[x] = find(s[x]);
}
//按高度求并, 使浅的树成为深的树的子树,
//当两棵相等的树求并时树的高度才增加1
void unionSets(int root1, int root2)
{
//root2更深一些,使root2成为新结点
if (s[root2] < s[root1])
s[root1] = s[root2];
else
{
if (s[root1] == s[root2])
s[root1]--;
s[root2] = root1;
}
}
private:
//存储每个子树的高度,如果是根节点,则存储高度
//实际上存储的是高度的负值,减去附加的1,初试时所有的项都是-1
//不是根节点则存储父节点的序号
vector<int> s;
};
int main()
{
DisjSets dset(10);
int root1, root2;
cout << "请输入1-10之间的数:" << endl;
while (cin >> root1 >> root2)
dset.unionSets(root1, root2);
cout << root1 << "的根是:" << dset.find(root1) << endl;
cout << root2 << "的根是:" << dset.find(root2) << endl;
system("pause");
return 0;
}
运行结果为:
第2种:运行结果是按照算法导论第三版 329中简单的图
#include <iostream>
#include <vector>
using namespace std;
template <typename DataType>
struct ArrayNode
{
DataType data;
ArrayNode *p; //结点的父亲
int rank; //结点的高度
ArrayNode() : p(NULL), rank(0) {}
};
template <typename DataType>
class ArraySet
{
public:
typedef ArrayNode<DataType> Node;
ArraySet(int num) : number(num)
{
}
//使每个结点成为一个单独的集合
void MakeSet()
{
cout << "请输入 " << number << "个数据:" << endl;
for (int i = 0; i < number; i++)
{
DataType val;
cin >> val;
Node *nd = new Node;
nd->p = nd;
nd->rank = 0;
nd->data = val;
vec.push_back(nd);
}
}
Node *getNode(int i)
{
return vec[i-1];
}
//带路径压缩的找到x所在的集合
Node* FindSet(Node *x)
{
if (x->p != x)
x->p = FindSet(x->p);
return x->p;
}
//合并集合,将rank值较小的集合合并到rank值较大的集合
void UnionSet(Node *nx, Node *ny)
{
Node *x = FindSet(nx);
Node *y = FindSet(ny);
if (x->rank > y->rank)
y->p = x;
else
{
x->p = y;
if (x->rank == y->rank) //当两者的rank相同,则使树根的rank增加1
(y->rank)++;
}
}
void display()
{
for (int i = 0; i < number; i++)
{
cout << "第 " << i+1 << " 个结点的数据为:" << vec[i]->data
<< " 父节点为:" << vec[i]->p->data << " 秩为:" << vec[i]->rank << endl;
}
cout << "*************************************************" << endl;
}
~ArraySet()
{
for (int i = 0; i < number; i++)
{
delete vec[i];
vec[i] = NULL;
}
}
private:
vector<Node *> vec;
int number;
};
int main()
{
ArraySet<char> as(6);
as.MakeSet();
as.UnionSet(as.getNode(1), as.getNode(2));
as.UnionSet(as.getNode(3), as.getNode(4));
as.UnionSet(as.getNode(5), as.getNode(6));
as.display();
as.UnionSet(as.getNode(2), as.getNode(4));
as.display();
as.UnionSet(as.getNode(4), as.getNode(6));
as.display();
system("pause");
return 0;
}
运行结果: