Mind and Hand Help

Union Find

并查集,用于快速定位元素是否在同一个集合中

定义通用接口

interface UnionFind<T> { fun isEmpty(): Boolean { return size() == 0 } fun size(): Int // 是否包含指定元素 fun contains(value: T): Boolean fun add(value: T): Boolean // union fun union(value1: T, value2: T): Boolean // 交集判断 fun isConnected(value1: T, value2: T): Boolean fun clear() }

实现

基于数组索引

基于Tree

  • 元素本身存在唯一ID

  • 元素本身能保证唯一性

Indexed Union Find

class IndexedUnionFind<T> : UnionFind<T> { companion object { val log = getLogger(IndexedUnionFind::class.java) } private val indexFunc: (T) -> Int private var data: Array<Any?> = arrayOfNulls<Any?>(2) // 存储data的索引的元素的父节点索引 private var parent = Array(2) { -1 } // 存储data的索引的元素的子节点索引列表 private var children = Array<MutableList<Int>>(2) { mutableListOf() } private var count: Int = 0 constructor(indexFunc: (T) -> Int) { this.indexFunc = indexFunc } override fun size(): Int = this.count override fun contains(value: T): Boolean { return false } override fun add(value: T): Boolean { val index = indexFunc(value) if (index < 0) { throw IllegalArgumentException("Index must be non-negative") } expand(index + 1) if (data[index] == null) { // 如果当前索引位置没有元素,则添加新元素 data[index] = value parent[index] = index count++ } else { // 如果当前索引位置已经有元素,替换 data[index] = value } return true } private fun expand(newSize: Int) { if (newSize > data.size) { val newData = Array<Any?>(newSize) { null } val newParent = Array(newSize) { -1 } val newChildren = Array<MutableList<Int>>(newSize) { mutableListOf() } System.arraycopy(data, 0, newData, 0, data.size) System.arraycopy(parent, 0, newParent, 0, parent.size) System.arraycopy(children, 0, newChildren, 0, children.size) data = newData parent = newParent children = newChildren } } override fun union(value1: T, value2: T): Boolean { val v1Index = indexFunc(value1) val v2Index = indexFunc(value2) if (v1Index < 0) { log.equals("Index for value1 must be non-negative") return false } if (v2Index < 0) { log.equals("Index for value2 must be non-negative") return false } if (v1Index == v2Index) { log.debug("Both values are the same, no union needed") return false } // update parent when add this.add(value1) this.add(value2) this.union(v1Index, v2Index) return true } private fun union(v1Index: Int, v2Index: Int) { val v1ParentIndex = getParent(v1Index) val v2ParentIndex = getParent(v2Index) if (v1ParentIndex == v2ParentIndex) { return } children[v1ParentIndex].add(v2ParentIndex) children[v1ParentIndex].addAll( children[v2ParentIndex].apply { this.forEach { // Update the parent of all children from v2 to v1 parent[it] = v1ParentIndex } } ) children[v2ParentIndex].clear() parent[v2ParentIndex] = v1ParentIndex parent[v2Index] = v1ParentIndex } private fun getParent(valueIndex: Int): Int { return if (valueIndex.indexInArr(parent)) { parent[valueIndex] } else { -1 } } private fun getParent(value: T): Int { val curIndex = indexFunc(value) return if (curIndex.indexInArr(parent)) { parent[curIndex] } else { -1 } } override fun isConnected(value1: T, value2: T): Boolean { val v1Index = indexFunc(value1) val v2Index = indexFunc(value2) if (!v1Index.indexInArr(parent) || !v2Index.indexInArr(parent)) { return false } val parent1 = getParent(value1) val parent2 = getParent(value2) if (parent1 == -1 || parent2 == -1) { return false } return parent1 == parent2 } override fun clear() { this.data = arrayOfNulls<Any?>(2) this.parent = Array(2) { -1 } this.children = Array(2) { mutableListOf() } this.count = 0 } internal fun <T> Int.indexInArr(arr: Array<T>): Boolean { return this >= 0 && this < arr.size } }

Tree Id Union Find

class TreeIdUnionFind<T, ID : Comparable<ID>>( private val identifierFunction: (T) -> ID ) : UnionFind<T> { private val storage: TreeMap<ID, Node> = TreeMap() override fun contains(value: T): Boolean { return doContains(value) } private fun doContains(data: T): Boolean { val id = getIdentifier(data) ?: return false return storage.containsKey(id) } override fun size(): Int { return storage.size } override fun add(value: T): Boolean { val identifier = getIdentifier(value) ?: return false return doAdd(Node(identifier), check = true) != null } private fun doAdd(addNode: Node, check: Boolean): Node? { val id = addNode.id return if (storage.containsKey(id)) { if (check) { null } else { storage[id] } } else { storage[id] = addNode addNode } } override fun union(value1: T, value2: T): Boolean { val xNode = doAdd(Node(getIdentifier(value1) ?: return false), check = false) val yNode = doAdd(Node(getIdentifier(value2) ?: return false), check = false) if (xNode == null || yNode == null) { return false } union(xNode, yNode) return true } private fun union(src: Node, cur: Node) { val srcParent = getParent(src) val curParent = getParent(cur) // 已经连接 if (idEquals(srcParent.id, curParent.id)) { return } // 树压缩 curParent.parent = srcParent srcParent.children.add(curParent) srcParent.children.addAll(curParent.children) curParent.children.forEach { child -> child.parent = srcParent } curParent.children.clear() } private fun getParent(node: Node): Node { return node.parent?.let { getParent(it) } ?: node } override fun isConnected(value1: T, value2: T): Boolean { if (!contains(value1) || !contains(value2)) { return false } val id1 = getIdentifier(value1)!! val id2 = getIdentifier(value2)!! val node1 = storage[id1]!! val node2 = storage[id2]!! return idEquals(getParent(node1).id, getParent(node2).id) } private fun getIdentifier(data: T): ID? { return identifierFunction(data) } // 先比较 equals 再比较 compareTo private fun idEquals(id1: ID, id2: ID): Boolean { return id1 == id2 || id1.compareTo(id2) == 0 } override fun clear() { storage.clear() } /** * 内部节点类 */ private inner class Node(val id: ID) { var parent: Node? = null val children: MutableList<Node> = mutableListOf() } }

Tree Union Find

class TreeUnionFind<T> : UnionFind<T> { private val comparator: Comparator<T> private val storage: TreeMap<T, Node<T>> constructor(comparator: Comparator<T>) { this.comparator = comparator this.storage = TreeMap(comparator) } override fun size(): Int = this.storage.size override fun contains(value: T): Boolean { return this.storage.containsKey(value) } override fun add(value: T): Boolean { if (this.storage.containsKey(value)) { return false } doAdd(value) return true } private fun doAdd(value: T): Node<T> { val node = storage[value] return if (node == null) { storage[value] = Node(value) storage[value]!! } else { node } } override fun union(value1: T, value2: T): Boolean { val node1 = doAdd(value1) val node2 = doAdd(value2) if (comparator.compare(node1.data, node2.data) == 0) { return false } val parent1 = getParent(node1) val parent2 = getParent(node2) if (comparator.compare(parent1.data, parent2.data) == 0) { return true } parent1.children.addAll(parent2.children.apply { forEach { it.parent = parent1 } }) parent2.children.clear() parent2.parent = parent1 parent1.children.add(parent2) return true } private fun getParent(node: Node<T>): Node<T> { var current = node while (current.parent != null) { current = current.parent!! } return current } override fun isConnected(value1: T, value2: T): Boolean { val node1 = storage[value1] ?: return false val node2 = storage[value2] ?: return false val parent1 = getParent(node1) val parent2 = getParent(node2) return comparator.compare(parent1.data, parent2.data) == 0 } override fun clear() { this.storage.clear() } inner class Node<T> { var parent: Node<T>? = null val data: T val children: MutableList<Node<T>> = mutableListOf() constructor(data: T) { this.data = data } } }
27 January 2026