并查集专题--Map和数组两种方式实现并查集(Java)

265 篇文章 2 订阅
33 篇文章 0 订阅

什么是并查集

并查集是一种数据结构,用于处理不相交集合的合并及查询问题。它是一种树型数据结构,每个节点代表一个集合,节点中存储该集合的所有元素,且每个节点指向它的子节点,按照元素的大小关系构成一个有向图。
在并查集中,查找操作可以在常数时间内完成,而合并操作则需要使用一定的算法进行优化,通常是使用哈希表来实现哈希冲突解决,以减少时间复杂度。
并查集在数据挖掘、图论等领域有广泛的应用,例如用于构建关联规则挖掘算法、社交网络分析等。

功能原理

在并查集里,我们要实现几个功能.
1.两个元素在不在同一个集合里.
2.当前集合的大小
3.把两个元素合并在一个集合里.
实现步骤:
A.我们在初始化并查集时,每个元素就是一个单独的集合,他的大小就是1.
B.判断两个元素是不是在同一个集合,是要判断两个元素的头节点或者是不是同一个节点,如果在同一个节点,就认定是同一个集合中,
C.合并两个集合时,我们先判断两个集合的大小,小的挂载在大的下面.

HashMap 实现并查集

	/**
	 * 对每个元素封装下
	 * @param <V>
	 */
	public static class Node<V> {
		V value;

		public Node(V v) {
			value = v;
		}
	}

	/**
	 * 用map 来实现并查集
	 * @param <V>
	 */
	public static class UnionFind<V> {
		public HashMap<V, Node<V>> nodes;
		//用map集合来代表指针,
		public HashMap<Node<V>, Node<V>> parents;
		public HashMap<Node<V>, Integer> sizeMap;

		public UnionFind(List<V> values) {
			nodes = new HashMap<>();
			parents = new HashMap<>();
			sizeMap = new HashMap<>();
			for (V cur : values) {
				Node<V> node = new Node<>(cur);
				nodes.put(cur, node);
				parents.put(node, node);
				sizeMap.put(node, 1);
			}
		}

		// 给你一个节点,请你往上到不能再往上,把代表返回
		public Node<V> findFather(Node<V> cur) {
			Stack<Node<V>> path = new Stack<>();
			while (cur != parents.get(cur)) {
				path.push(cur);
				cur = parents.get(cur);
			}
			//每次查询时 优化下结构,子节点直接挂载在头节点下
			while (!path.isEmpty()) {
				parents.put(path.pop(), cur);
			}
			return cur;
		}

		public boolean isSameSet(V a, V b) {
			return findFather(nodes.get(a)) == findFather(nodes.get(b));
		}

		/**
		 * 合并两个节点
		 * @param a
		 * @param b
		 */
		public void union(V a, V b) {
			Node<V> aHead = findFather(nodes.get(a));
			Node<V> bHead = findFather(nodes.get(b));
			//不在同一个集合时 才去合并
			if (aHead != bHead) {
				//获取大小,小的挂载在大的下面
				int aSetSize = sizeMap.get(aHead);
				int bSetSize = sizeMap.get(bHead);
				Node<V> big = aSetSize >= bSetSize ? aHead : bHead;
				Node<V> small = big == aHead ? bHead : aHead;
				parents.put(small, big);
				sizeMap.put(big, aSetSize + bSetSize);
				//挂载后,删除掉大小记录
				sizeMap.remove(small);
			}
		}

		/**
		 * 集合的个数
		 * @return
		 */
		public int sets() {
			return sizeMap.size();
		}

	}

用数组实现并查集(效率更高)

public class UnionFind {

	
	public static int[] father = new int[10000];

	public static int[] size = new int[10000];

	public static int[] help = new int[10000];

	// 初始化并查集
	public static void init(int n) {
		for (int i = 0; i <= n; i++) {
			father[i] = i;
			size[i] = 1;
		}
	}

	// 从i开始寻找集合代表点
	public static int find(int i) {
		int hi = 0;
		while (i != father[i]) {
			help[hi++] = i;
			i = father[i];
		}
		//优化结构
		for (hi--; hi >= 0; hi--) {
			father[help[hi]] = i;
		}
		return i;
	}

	// 查询x和y是不是一个集合
	public static boolean isSameSet(int x, int y) {
		return find(x) == find(y);
	}

	// x所在的集合,和y所在的集合,合并成一个集合
	public static void union(int x, int y) {
		int fx = find(x);
		int fy = find(y);
		if (fx != fy) {
			if (size[fx] >= size[fy]) {
				size[fx] += size[fy];
				father[fy] = fx;
			} else {
				size[fy] += size[fx];
				father[fx] = fy;
			}
		}
	}

}

可以在这个链接上去测试上面实现的代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值