当前位置: 首页 > article >正文

数据结构--并查集

并查集

  • 原理
  • 实现

原理

在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。此后我们可能要反复用到查询某一个元素归属于那个集合的运算,适合于描述这类问题的抽象数据类型称为并查集。

并查集的本质就是一个森林,属于同一个集合的元素在同一颗树中,我们可以采用一个数组来描述这个森林:数组下标对应元素的编号,该下标对应的数组值表示该元素的父亲节点的下标,如果为负值-1,表示当前元素是根节点。

开始时,每个元素自成一个单元素集合,然后按一定的规律将归于同一组元素的集合合并,为了查询效率,应该在合并时让数据量小的集合向数据量大的集合合并(这样就是让数据量小的树的元素高度增加,而数据量大的树的元素的高度不变),同时为了避免某棵树的高度过高,我们还需要进行路径压缩,可以选择在查询某个元素时直接将该元素连接到根节点下面,这样下次查询时速度就很快了。

我们这里实现时数组的下标对应的是元素的编号,而我们原本的数据类型是不确定的,因此我们需要将原来的数据类型映射到数组下标上:只需要将数据插入到一个数组当中,这样原始数据就具有了下标,同时我们还可以通过下标直接找到原始数据;为了还可以通过原始数据找到下标,我们可以通过关联式容器map建立原始数据到其下标的映射。

实现

#include<iostream>
#include<map>
#include<vector>
#include<string>

using namespace std;

template<class T>
class unionFindSet {
public:
	unionFindSet(vector<T>& datas)
		:_elements(datas)
		,_union(datas.size(),-1)
	{
		int i = 0;
		for (; i < _elements.size(); ++i) {
			_index.insert(make_pair(_elements[i], i));
		}
	}

	~unionFindSet()
	{}

	//检查2个元素是否在同一个集合当中
	bool IsInOneSet(T data1, T data2) {
		int root1 = FindRoot(data1);
		int root2 = FindRoot(data2);
		if (-1 == root1 || -1 == root2) {
			return false;
		}

		return root1 == root2;
	}

	//合并2个集合
	bool Union(T data1, T data2) {
		int root1 = FindRoot(data1);
		int root2 = FindRoot(data2);
		if (-1 == root1 || -1 == root2) {
			return false;
		}

		_union[root1] = root2;
		return true;
	}

	//查找集合的个数
	int Count() {
		int ans = 0;
		for (auto e : _union) {
			if (-1 == e) {
				++ans;
			}
		}

		return ans;
	}
private:
	//返回元素data的根节点
	int FindRoot(T& data) {
		if (_index.end() == _index.find(data)) {
			return -1;
		}
		int pos = _index[data];
		while (-1 != _union[pos]){
			pos = _union[pos];
		}

		if (pos != _index[data]) {
			//将该元素连接到根节点下面
			_union[_index[data]] = pos;
		}
		return pos;
	}


private:
	vector<int> _union;				//集合
	vector<T> _elements;			//将原始数据和下标建立关系的数组
	map<T, int> _index;	//利用原始数据寻找它在_elemrnts中的下标的关联式容器
};

int main() {
	vector<string> elements = { "alice","bob","jone","jason","tom","jerry","willon","michael" };
	unionFindSet<string> s(elements);
	printf("count: %d\n", s.Count());
	printf("is %s and %s in one set:%d\n", "alice", "bob", s.IsInOneSet("alice", "bob"));
	s.Union("alice", "bob");
	printf("is %s and %s in one set:%d\n", "alice", "bob", s.IsInOneSet("alice", "bob"));
	printf("count: %d\n", s.Count());
	printf("is %s and %s in one set:%d\n", "alice", "tom", s.IsInOneSet("alice", "tom"));
	s.Union("alice", "tom");
	printf("is %s and %s in one set:%d\n", "tom", "bob", s.IsInOneSet("tom", "bob"));
	printf("count: %d\n", s.Count());
	return 0;
}

http://www.kler.cn/a/406340.html

相关文章:

  • 【Unity服务】如何集成Unity广告(Legacy)
  • 淘宝商品评论爬虫:Java实现指南
  • PostGIS分区表创建
  • 深入探究蓝牙节能技术:SNIFF与HOLD模式
  • Python操作neo4j库py2neo使用之py2neo 删除及事务相关操作(三)
  • 002 MATLAB语言基础
  • 比rsync更强大的文件同步工具rclone
  • 解析粗糙度仪在工业制造及材料科学和建筑工程领域的重要性
  • 半导体工艺与制造篇5 光刻
  • 40分钟学 Go 语言高并发:并发下载器开发实战教程
  • 「Chromeg谷歌浏览器/Edge浏览器」篡改猴Tempermongkey插件的安装与使用
  • 倒计时功能分享
  • 数据结构-8.Java. 七大排序算法(上篇)
  • Linux 手动升级软件保姆级教程,适用所有软件,不限于麒麟等国产系统
  • 【Golang】协程
  • 迁移学习理论与应用
  • 力扣--LRC 142.训练计划IV
  • Ubuntu ESP32开发环境搭建
  • 五天SpringCloud计划——DAY2之使用Docker完成项目的部署
  • Excel的图表使用和导出准备
  • [面试]-golang基础面试题总结
  • redis7.x源码分析:(4) ae事件处理器(一)
  • 《Django 5 By Example》阅读笔记:p645-p650
  • SQL注入:理解、防范与最佳实践
  • Ubuntu安装Electron环境
  • 学习electron