1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or is a part of
11 # union–find algorithm using an efficient disjoint-set data structure
14 import hash_collection
16 # Data structure to keeps track of elements partitioned into disjoint subsets
17 # var s = new DisjointSet[Int]
20 # assert not s.in_same_subset(1,2)
22 # assert s.in_same_subset(1,2)
24 # `in_same_subset` is transitive, reflexive and symetric
27 # assert not s.in_same_subset(1,3)
29 # assert s.in_same_subset(1,3)
31 # Unkike theorical Disjoint-set data structures, the underling implementation is opaque
32 # that makes the traditionnal `find` method unavailable for clients.
33 # The methods `in_same_subset`, `to_partitions`, and their variations are offered instead.
34 class DisjointSet[E
: Object]
35 super SimpleCollection[E
]
37 # The node in the hiearchical structure for each element
38 private var nodes
= new HashMap[E
, DisjointSetNode]
40 # Get the root node of an element
42 private fun find
(e
:E
): DisjointSetNode
44 assert nodes
.has_key
(e
)
46 if ne
.parent
== ne
then return ne
52 # Get the root node of a node
53 # Use *path compression* to flatten the structure
54 # ENSURE: `result.parent == result`
55 private fun nfind
(ne
: DisjointSetNode): DisjointSetNode
58 if nf
== ne
then return ne
64 # Is the element in the structure
66 # var s = new DisjointSet[Int]
71 redef fun has
(e
: E
): Bool
73 return nodes
.has_key
(e
)
76 redef fun iterator
do return nodes
.keys
.iterator
78 # Add a new element in the structure.
79 # Initially it is in its own disjoint subset
84 if nodes
.has_key
(e
) then return
85 var ne
= new DisjointSetNode
89 # Are two elements in the same subset?
90 fun in_same_subset
(e
,f
:E
): Bool
92 return e
== f
or find
(e
) == find
(f
)
95 # Are all elements of `es` in the same subset?
96 # var s = new DisjointSet[Int]
97 # s.add_all([1,2,3,4,5,6])
98 # s.union_all([1,2,3])
99 # assert not s.all_in_same_subset([2,3,4])
100 # s.union_all([1,4,5])
101 # assert s.all_in_same_subset([2,3,4])
102 fun all_in_same_subset
(es
: Collection[E
]): Bool
104 if es
.is_empty
then return true
105 var nf
= find
(es
.first
)
108 if ne
!= nf
then return false
113 # Construct the current partitionning
115 # var s = new DisjointSet[Int]
116 # s.add_all([1,2,3,4,5,6])
120 # var p = s.to_partitions
121 # assert p.length == 3
122 fun to_partitions
: Collection[Set[E
]]
124 return to_subpartition
(self)
127 # Construct a partitionning on `es`, a subset of elements
129 # var s = new DisjointSet[Int]
130 # s.add_all([1,2,3,4,5,6])
134 # var p = s.to_subpartition([1,2,4])
135 # assert p.length == 2
136 fun to_subpartition
(es
: Collection[E
]): Collection[Set[E
]]
138 var map
= new HashMap[DisjointSetNode, Set[E
]]
141 var set
= map
.get_or_null
(ne
)
151 # Combine the subsets of `e` and `f`
152 # ENSURE: `in_same_subset(e,f)`
157 if ne
== nf
then return
159 # merge them using *union by rank*
160 # attach the smaller tree to the root of the deeper tree
170 # The only case where the deep is increased is when both are equals
176 # Combine the subsets of all elements of `es`
177 # ENSURE: `all_in_same_subset(cs)`
178 fun union_all
(es
:Collection[E
])
180 if es
.is_empty
then return
182 for e
in es
do union
(e
,f
)
186 # A node in the hierarchical representation of subsets
187 private class DisjointSetNode
188 # If parent == self then the node is a root
189 var parent
: DisjointSetNode = self
191 # The rank to no desequilibrate the structure.
192 # The term rank is used instead of depth since
193 # path compression is used, see `DisjointSet::nfind`