C++ tree advanced series is not an equivalent class that a family does not enter a family

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.

1.png

And the following figure is a disconnected graph. If there are 2connected components, all vertices are considered to be distributed in 2equivalence classes. A connected component is an equivalence class. So equivalence classes can be used to judge the connectivity of graphs.

2.png

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 selements 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 Rthe 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.

3.png

  • 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 elements 1and 3the current belong to 2different equivalence classes, and 2the equivalence classes are merged. When one equivalence class is merged into another equivalence class, one of the equivalence classes becomes empty.

4.png

  • 2Repeat 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 is 2.

5.png

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.

6.png

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 1and 3the 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:

8.png

When 2the 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.

9.png

10.png

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 2sub-logic:

  • The equivalence class of the query element.
  • Merge different equivalence classes.

When using code to implement, you need 2a function to implement this 2sub- 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:

11.png

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.

12.png

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.

13.png

//存储等价类信息的数组
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,3shown in the figure below.

  • The linked list where the element 3is located is inserted 1behind the head node of the linked list where the element is located in the head insertion mode.

14.png

  • 3The value between the array cells of the head node of the linked list where the original storage element is located is set to NULL.

15.png

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:

16.png

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).

17.png

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:

18.png

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.

Guess you like

Origin blog.csdn.net/y6123236/article/details/129492537