1 Introduction
What is an equivalence class?
A class, as the name suggests, is a description of a group of objects with common characteristics, and a class can also be called a collection here.
If there is a set, all objects (elements, data) in the set are said to satisfy the equivalence relation. Described from another perspective, when the objects or elements are not in the same set, the equivalence relationship is not satisfied.
图论
The connectivity of a graph can be described using an equivalence relation in .
As shown in the figure below, any two vertices are connected, and this graph is called a connected graph, and connectivity is a characteristic of the graph. And it can be considered that all the vertices in this graph are in one set, and there is an equivalence relationship, and everyone is in one equivalence class.
And the following figure is a disconnected graph. If there are 2
connected components, all vertices are considered to be distributed in 2
equivalence classes. A connected component is an equivalence class. So equivalence classes can be used to judge the connectivity of graphs.
2. Equivalence class relationship
How to confirm that the element belongs to the same equivalence class as the element?
That is to say, if there is a collection with s
elements n
, how to determine whether the elements are in one equivalence class or distributed in different equivalence classes.
Of course, when determining whether elements belong to the same equivalence class, some relationship information between elements is needed. Generally use (x,y)
the form of the form, also known as the equivalent pair.
For example, existing s={1,2,3,4,5,6,7,8}
, and there is an equivalence pair relationship
R = {(1,3),(3,5),(3,7),(2,4),(4,6),(2,8)}
. Please find R
the equivalence class of s based on .
The basic idea is as follows:
- Initially, it is assumed that each element in the original set is an equivalence class containing only itself.
- The pair relations are read sequentially, and the equivalence classes with the pair relations are merged. In the figure below, the
(1,3)
pair relationship is read first, the elements1
and3
the current belong to2
different equivalence classes, and2
the equivalence classes are merged. When one equivalence class is merged into another equivalence class, one of the equivalence classes becomes empty.
2
Repeat the above process, according to the read-in pair relationship, if the elements in the pair relationship belong to different equivalence classes, then merge them; if they belong to the same equivalence class, keep the status quo. As shown in the figure below, the final equivalence class is2
.
According to the above search equivalence class, we can know that:
- Equivalence classes allow direct or indirect relationships between elements.
- When merging, equivalence classes will become fewer and fewer.
3. Realization of equivalence classes
An equivalence class can be regarded as a set, and multiple equivalence classes form a set group. In order to distinguish different sets (equivalence classes) in the set group, it is necessary to set a unique symbol for each equivalence class.
Equivalence classes can be implemented using arrays, trees, and linked lists. Either way, you need to set a unique glyph for the equivalence class.
3.1 Array implementation
Use arrays to solve the problem of finding equivalence classes in the above case.
3.1.1 Initializing the array
At the beginning, a one-dimensional array is created, because at the beginning, it is unknown how many equivalence classes there will be. Initially it can be assumed that a number belongs to an equivalence class, and the symbol of the equivalence class is the number itself. As shown below:
Tips: The equivalence class of the number is the same subscript position as the number in the array.
The above process can be described by code:
#include <iostream>
using namespace std;
//存储等价类信息的数组
int nums[9]= {0};
/*
* 初始化函数
*/
void init() {
for(int i=1; i<9; i++) {
nums[i]=i;
}
}
3.1.2 Merging equivalence classes
The merging of equivalence classes is based on the result of querying pair relations.
If you read in (1,3)
a pair relationship, you need to query the elements 1
and 3
the equivalence classes they belong to. If they belong to the same equivalence class, do nothing. If they belong to different equivalence classes, they will be merged. As shown below:
When 2
the sets are merged, they can be merged in both directions. In actual operation, you can choose one. In this way, the following two effects will appear.
How to judge how many equivalence classes there are from the above figure?
The number of equivalence classes can be calculated by finding how many cells have the same subscript and stored value.
In the above description, including 2
sub-logic:
- The equivalence class of the query element.
- Merge different equivalence classes.
When using code to implement, you need 2
a function to implement this 2
sub- logic.
/*
*查找元素所属等价类的标志符号
*/
int find(int data) {
while(nums[data]!=data)
data=nums[data];
return data;
}
/*
*合并等价类
*/
void unionSet(int data,int data_) {
//查找所属等价类
int flag= find(data);
int flag_=find(data_);
if(flag!=flag_) {
//合并
//nums[flag_]=flag;
//或者
nums[flag]=flag_;
}
}
Test generation merge process:
/*
*测试
*/
int main(int argc, char** argv) {
init();
int r[6][2] = {
{1,3},{3,5},{3,7},{2,4},{4,6},{2,8}};
for(int i=0; i<6; i++) {
unionSet(r[i][0],r[i][1] );
}
cout<<"数字:"<<endl;
for(int i=1; i<9; i++) {
cout<<i<<"\t";
}
cout<<endl;
cout<<"等价类"<<endl;
for(int i=1; i<9; i++) {
cout<<nums[i]<<"\t";
}
return 0;
}
Output result:
3.2 Linked list
There is no difference between the high-level logic of linked lists and arrays, and the difference lies in the underlying storage methods.
When an array is stored, a cell of the initial array is an equivalence class. When using linked list storage, the initial element (data) is a linked list. The symbol of the equivalence class can be stored at the head node of the linked list, which is initially the number itself.
When using code description, it is necessary to construct the node type.
#include <iostream>
using namespace std;
/*
*结点类型
*/
struct Node {
//数据域
int data;
//指向后一个指针
Node *next;
};
In order to facilitate management, a one-dimensional array is used to store the head node addresses of all linked lists. and initialize it.
//存储等价类信息的数组
Node* nums[9];
/*
* 初始化函数
*/
void init() {
for(int i=1; i<9; i++) {
nums[i]=new Node();
nums[i]->data=i;
nums[i]->next=NULL;
}
}
Since it is necessary to merge data that are not in the same equivalence class, it is also necessary to provide a search function to find the equivalence class in which a given element is located.
/*
* 查找元素所属等价类的标志符号
* 因头结点存储标志符号
* 函数返回数据所在链表的头结点
*/
Node* find(int data) {
for(int i=1; i<9; i++) {
if(nums[i]==NULL)continue;
Node* move=nums[i];
while(move!=NULL && move->data!=data ) {
move=move->next;
}
if(move==NULL)continue;
else return nums[i];
}
}
In addition, a merge function must be provided. For example, the process of merging linked lists where elements are located according to the pair relationship (1,3)
is 1,3
shown in the figure below.
- The linked list where the element
3
is located is inserted1
behind the head node of the linked list where the element is located in the head insertion mode.
3
The value between the array cells of the head node of the linked list where the original storage element is located is set toNULL
.
The merge function is as follows:
/*
*合并等价类
*/
void unionSet(int data,int data_) {
Node * flag= find(data);
Node * flag_= find(data_);
if( flag->data!=flag_->data ) {
//合并
flag_->next=flag->next;
nums[flag_->data]=NULL;
flag->next=flag_;
}
}
Test code:
/*
*测试
*/
int main(int argc, char** argv) {
init();
int r[6][2] = {
{1,3},{3,5},{3,7},{2,4},{4,6},{2,8}};
for(int i=0; i<6; i++) {
unionSet(r[i][0] ,r[i][1]);
}
for(int i=1; i<9; i++) {
if(nums[i]!=NULL) {
Node * move=nums[i];
cout<<"等价类:"<<move->data<<endl;
while(move!=NULL){
cout<<move->data<<"\t";
move=move->next;
}
cout<<endl;
}
}
return 0;
}
Test Results:
3.3 tree
There is not much essential difference between using a tree and using a linked list. The tree itself is an abstract data structure. Linked list or array can be selected for physical storage. Therefore, compared with the previous linked list storage, the change is only in the logical difference of type merging.
This article still uses a linked list to describe the physical structure of the tree.
And the linked list has the concept of the head node, and the tree has the corresponding concept of the root node. Initially create multiple trees rooted at the number, and use the root of the tree to store the unique identifier of the equivalence class (that is, the value itself).
For the convenience of merging, when designing the nodes of the tree, in addition to setting the data field, it is also necessary to set a pointer field pointing to the parent pointer. This is slightly different from the node type of the design linked list. The purpose is to facilitate the query from the child node to the parent node, and find the root node of the tree where the element is located.
#include <iostream>
using namespace std;
//树结点类型
struct Node{
//数据域
int data;
//指向父指针的指针域
Node *parent;
};
Similarly, you can use a one-dimensional array to store the root nodes of all trees and initialize them.
Node* trees[9];
//初始化
void init() {
for(int i=1; i<9; i++) {
trees[i]=new Node();
trees[i]->data=i;
//根结点的父指针指向自己
trees[i]->parent=trees[i];
}
}
Provides query functions. The logic of the query function, query all the way up from the given node. Return the root node.
/*
*查询
*/
Node* find(int data) {
Node * move=trees[data];
while(move->parent->data!=data ) {
move=move->parent;
}
return move->parent;
}
Merge function:
void unionSet(int data,int data_) {
Node * flag= find(data);
Node * flag_= find(data_);
if( flag->data!=flag_->data ) {
//合并
flag_->parent=flag;
}
}
Test code:
//测试
int main(int argc, char** argv) {
init();
int r[6][2] = {
{1,3},{3,5},{3,7},{2,4},{4,6},{2,8}};
for(int i=0; i<6; i++) {
unionSet(r[i][0] ,r[i][1]);
}
for(int i=1; i<9; i++) {
if( trees[i]->parent->data==i ) {
cout<<"等价类"<<trees[i]->parent->data<<endl;
}
}
return 0;
}
Test Results:
4. Summary
This article explains what an equivalence class is and the characteristics of an equivalence class.
Elements in an equivalence class have direct and indirect relationships. It also means that the relationship between elements in an equivalence class is transitive.
After understanding the characteristics of the equivalence class, even if the means of realization are diversified, they still remain the same, and the inherent logic is the same. The difference lies in the way the code is expressed. Of course, there are differences in application scenarios.
The pure array scheme is suitable for situations where the data itself is not complex, and the tree and linked list schemes are suitable for situations where the data itself is more complex. Because the linked list is a linear data structure, it is suitable for one-to-one complex data scenarios, and the tree is suitable for one-to-many complex data. Application scenarios.