前言

前几天想把并查集这个结构搞清楚,但一直没有动笔写。
因为要有输出,印象才会更深刻些。
这里只是非常简单的记录一下。

并查集

并查集,顾名思义,最主要的2大功能就是并(合并),查(查找)。
合并 : 将2个不在同一个集合内的元素 所在的集合 合并在同一个集合。
查找 : 看看2个元素是否在 同一个集合内。
但是在 新建一个这个结构的时候,就必须把全部元素拿给他,也就是说,这种结构只能处理事先已经知道全部元素是什么的情况,不能一个一个的元素给他,不能处理 “流” 这种情况。

主要功能实现

  1. 初始化 : 在给了所有元素之后,各自元素单独成一个集合,也就是自己指向自己,自己的父节点是自己。自己所在的集合的代表节点就是自己。
  2. 找最头的那个节点,就是找一个集合的代表节点: 递归下去,有优化途径,就是在找的时候,以后的每个“父节点”都指向这个集合的代表节点(就是那个最终要找的节点)。也就是路径压缩,把长链给打扁平了,方便后续的查找操作,要不然的话性能会慢些许。
  3. 合并: 将2个元素的所在的集合合并成一个集合,以后再查的时候,这2个集合里的所有元素都是在一起的。在合并的时候,为性能优化,就把集合里元素少的那个集合的代表节点 指向 集合里元素多的那个集合的代表节点。

代码:

// 取一个类Node只是方便后续代码实现, 具体这个Node类里是什么都可以
	public static class Node{
   
		int i;
		public Node(int i) {
   
			this.i = i;
		}
	}
	
	public static class UnionFind {
   
		private HashMap<Node, Node> fatherMap;	// key 是当前节点; value 是当前节点的 父亲,一直往上找,即这个集合的代表节点
		private HashMap<Node, Integer> sizeMap;	// 这个节点所在的集合的节点个数有多少
		
		public UnionFind(List<Node> nodes) {
   
			makeSet(nodes);
		}
		
		private void makeSet(List<Node> nodes) {
   
			fatherMap = new HashMap<Node, Node>();
			sizeMap = new HashMap<Node, Integer>();
			for(Node n : nodes) {
   
				fatherMap.put(n, n);
				sizeMap.put(n, 1);
			} 
		}
		
		private Node findFather(Node node) {
   
			Node father = fatherMap.get(node);
			if(father != node) {
   
				father = fatherMap.get(father);
			}
			fatherMap.put(node, father);
			return father; 
		}
		
		public boolean isSameSet(Node a, Node b) {
   
			return findFather(a) == findFather(b);
		}
		
		public void union(Node a, Node b) {
   
			if(a == null || b == null) {
   
				return; 	
			}
			Node aHead = findFather(a);
			Node bHead = findFather(b);
			if(aHead == bHead) {
   
				return;
			}
			
			int aSetSize = sizeMap.get(aHead);
			int bSetSize = sizeMap.get(bHead);
			if(aSetSize <= bSetSize) {
   
				fatherMap.put(aHead, bHead);
				sizeMap.put(bHead, aSetSize+bSetSize);
			} else {
   
				fatherMap.put(bHead, aHead);
				sizeMap.put(aHead, aSetSize+bSetSize);
			} 
		}
		 
	}