实验4 在线等价类(并查集)

0x01 实验目的

掌握在线等价类的使用,要求使用模拟指针实现。

0x02 实验内容

  1. 使用模拟指针实现本实验。
  2. 输入一个1-9的正整数n,代表要创建n个元素,例如输入5,则代表创建一个1,2,3,4,5组成的元素表。
  3. 再输入一个大于0正整数r,代表后面要输入r个等价关系。
  4. 分行输入r个等价关系,格式如(1,2)。
  5. 分行输出所有等价类,一个等价类的元素由小到大依次输出。例如等价类(1,3,5,2,4),输出时排序输出(1,2,3,4,5)。
  6. 如果输出不是由小到大顺序输出,解决办法很多,可以创建一个n*n的数组,一行存放一个等价类,将所有等价类放入数组,对数组每一行元素进行排序,输出数组所有元素。

0x03 实验过程

路径压缩

采用递归的形式,将链状的结构,变成树状,每一个节点的指针域都存的是自己的祖先节点,而不仅是父节点。

int Find(int i) {
	if(i == node[i].next) return i;
	else {
		node[i].next = Find(node[i].next);
		return node[i].next;
	}
}

合并判断&合并顺序

  • 在合并两个类时,要判断它们的祖宗节点是否是同一个,如果是就不再合并,不是才合并。
  • order is important:必须确保两个数合并,必须由小的数指向大的数,因为在后面遍历时,如果是大的指向小的,而小的又没指向其他节点时,因为从小到大遍历的局限性,会直接将小的数当成单个类直接输出。
void unite(int m,int n) {
	//order is important
	if(m > n) swap(m,n);
	int i = Find(m);
	int j = Find(n);
	if(i != j) node[i].next = j;
}

输出判断顺序

	for(int i = 1; i <= num; i++) {
		//order is important?
		if(Find(i) != i && arr[i] == false) {
			//cout<
			int sum = 0;
			for(int j = 1; j <= num; j++) {
				//first 1 is special
				if(arr[j] == true) continue;
				//first is easy
				if(sum == 0) {
					cout<<"(";
					cout<<j;
					arr[j] = true;
				}
				if(Find(j) == Find(i) && sum != 0) {
					cout<<","<<j;
					arr[j] = true;
				}
				sum++;
			}
			if(sum != 0) {
				cout<<")"<<endl;
			}
		} else {
			if(Find(i) == i && arr[i] == false) {
				cout<<"("<<i<<")"<<endl;
				arr[i] = true;
			}
		}
	}

括号输出问题

先输出 然后逗号跟在数字前输出,比逗号跟在数字后输出好:把第一个当成特殊情况,比把最后一个当成特殊情况要好。(最后一个在不知数量的情况下,不好判断)

if(sum == 0) {
		cout<<"(";
		cout<<j;
		arr[j] = true;
	}
if(Find(j) == Find(i) && sum != 0) {
		cout<<","<<j;
		arr[j] = true;
}

0x04 完整源码

参考视频:图论-并查集(Bilibli)

#include 
using namespace std;

//定义节点
struct Node {
	int value,next;
};
Node *node;
int num;

//记录是否已经被使用过
bool arr[100];

//初始化,每个元素都指向自己,且值为0
void init(int sum) {
	num = sum;
	node = new Node [num + 1];
	for(int i = 0; i <= num; i++) {
		node[i].next = i;
		node[i].value = 0;
	}
}

//查询祖宗节点操作
int Find(int i) {
	if(i == node[i].next) return i;
	else {
		node[i].next = Find(node[i].next);
		return node[i].next;
	}
}

//合并节点操作
void unite(int m,int n) {
	//order is important
	if(m > n) swap(m,n);
	int i = Find(m);
	int j = Find(n);
	if(i != j) node[i].next = j;
}

int main() {
	cout<<"Input"<<endl;
	cin>>num;
  //初始化num个节点
	init(num);
	int p;
	cin>>p;
  //p个等价关系
	for(int i = 0; i < p; i++) {
		string s = "";
		cin>>s;
		unite(int(s.at(1)) - 48,int(s.at(3)) - 48);
	}
	cout<<"Output"<<endl;
	for(int i = 1; i <= num; i++) {
		//order is important
		if(Find(i) != i && arr[i] == false) {
			//cout<
			int sum = 0;

			for(int j = 1; j <= num; j++) {
				//first 1 is special
				if(arr[j] == true) continue;
				//first is easy
				if(sum == 0) {
					cout<<"(";
					cout<<j;
					arr[j] = true;
				}
				if(Find(j) == Find(i) && sum != 0) {
					cout<<","<<j;
					arr[j] = true;
				}
				sum++;
			}
			if(sum != 0) {
				cout<<")"<<endl;
			}
		} else {
			if(Find(i) == i && arr[i] == false) {
				cout<<"("<<i<<")"<<endl;
				arr[i] = true;
			}
		}

	}
	cout<<"End"<<endl;
	return 0;
}

你可能感兴趣的:(#,数据结构实验,数据结构)