c++实现并查集 并进行优化(一)

本文对比了两种并查集算法的实现方式,一种使用数组直接修改元素值,时间复杂度为O(n),另一种通过查找根节点进行连接,提高了查找效率。在10,000和100,000次操作的测试中,后者表现更优。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们首先实现的版本一的并查集,用数组实现,在Union(p,q)的操作中,我们遍历一遍数组,将值和p相同的元素的值都改为q所对应的值。

这样的话,我们的Union操作实际上时间复杂度是O(n)

版本二中,我们将在Union(p,q)的操作中,找到p和q的根节点,让其中一个的根结点指向另一个元素的根节点即可(指向根节点的原因是我们想尽量减少树的高度,从而在查找的时候效率更高。)

在测试10000 和 100000的数据中我们分别比较两种算法的性能,结果如下:

在这里插入图片描述

UnionFind1.h

#ifndef UNION_FIND1_H_
#define UNION_FIND1_H_


#include<iostream>
#include<cassert>

using namespace std;

namespace UF1
{

	class UnionFind
	{
	private:
		int* id;
		int count;

	public:
		UnionFind( int n)
		{
			count = n;
			id = new int[n];
			for(int i = 0; i< n ; i++)
			{
				id[i] = i;
			}
		}

		~UnionFind()
		{
			delete [] id;
		}

		int find(int p)
		{
			assert(p >= 0 && p < count);
			return id[p];
		}

		bool isConnected(int p , int q)
		{
			return find(p) == find(q);
		}

		void unionElements(int p, int q)
		{
			int pID = find(p);
			int qID = find(q);

			if(pID == qID)
				return;

			for(int i = 0 ;i < count ; i++) //这里用的是 i < count 不是 i < n 
			{
				if(id[i] == pID)
					id[i] = qID;
			}
		}
	};

};

#endif

UnionFind2.h

#ifndef UNION_FIND2_H_
#define UNION_FIND2_H_

#include<iostream>
#include<cassert>

namespace UF2
{
	class UnionFind
	{
	private:
		int* parent;
		int count;

	public:
		UnionFind(int count)
		{
			this->count = count;
			parent = new int[count]; 
			for(int i = 0 ; i < count ; i++)
				parent[i] = i;

		}

		~UnionFind()
		{
			delete [] parent;
		}

		int find(int p)
		{
			assert(p < count && p >= 0);
			while( p != parent[p]) 
			{
				p = parent[p];
			}

			return p;
		}

		void unionElements(int p , int q)
		{
			int pRoot = find(p);
			int qRoot = find(q);

			if( pRoot == qRoot)
				return;

			parent[pRoot] = qRoot;
		}

		bool isConnected(int p , int q)
		{
			return find(p) == find(q);
		}


	};
};

#endif

UnionFindTestHelper.h

#ifndef UNION_FIND_TEST_HELPER_H_
#define UNION_FIND_TEST_HELPER_H_

//学习一下测试用例的写法
#include<iostream>
#include<ctime>
#include"UnionFind1.h"
#include"UnionFind2.h"


using namespace std;

namespace UnionFindTestHelper
{
	void testUF1( int n)
	{
		srand(time(NULL));
		UF1::UnionFind uf = UF1::UnionFind(n);
		time_t startTime = clock();

		for(int i = 0; i < n; i++)  //O(n^2)
		{
			int a = rand()%n;
			int b = rand()%n;
			uf.unionElements(a,b); //O(n)
		}

		for(int i = 0; i < n; i++)
		{
			int a = rand()%n;
			int b = rand()%n;
			uf.isConnected(a,b);
		}

		time_t endTime = clock();

		cout << "UF1: " << 2*n << "ops, " << double(endTime-startTime) / CLOCKS_PER_SEC << " s. " << endl;
	}

	void testUF2( int n)
	{
		srand(time(NULL));
		UF2::UnionFind uf = UF2::UnionFind(n);
		time_t startTime = clock();

		for(int i = 0; i<n ; i++)
		{
			int a = rand()%n;
			int b = rand()%n;
			uf.unionElements(a,b);
		}

		for(int i = 0; i<n ; i++)
		{
			int a = rand()%n;
			int b = rand()%n;
			uf.isConnected(a,b);
		}

		time_t endTime = clock();

		cout << "UF2: " << 2*n << "ops, " << double(endTime-startTime) / CLOCKS_PER_SEC << " s. " << endl;
	}
};





#endif

main.cpp

#include<iostream>
#include "UnionFindTestHelper.h"

int main(int argc, char const *argv[])
{
	int n = 100000;

	UnionFindTestHelper::testUF1(n);
	UnionFindTestHelper::testUF2(n);


	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五月的天气

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值