1
+ package src .main .java .com .dataStructures ;
2
+
3
+ import java .io .Serializable ;
4
+ import java .util .*;
5
+
6
+ /**
7
+ * An implementation of disjoint-set, represented by rooted trees.
8
+ * <p>
9
+ * Actually, the instance of {@link DisjointSet} is a disjoint-set forests.
10
+ *
11
+ * <p>
12
+ * Disjoint-set operations:
13
+ * <p>
14
+ * 1. quickly unites two sets into a new set, requiring O(1) time.
15
+ * <p>
16
+ * 2. quickly query two elements whether contained in the same set, requiring about O(1) time.
17
+ *
18
+ * @author yangxf
19
+ */
20
+ public class DisjointSet <T > implements Serializable {
21
+ private static final long serialVersionUID = 3134700471905625636L ;
22
+
23
+ private Map <T , Node <T >> nodeMap = new HashMap <>();
24
+
25
+ /**
26
+ * Add an element to the disjoint-set forests as a set.
27
+ */
28
+ public void makeSet (T element ) {
29
+ checkNotNull (element , "element" );
30
+ nodeMap .putIfAbsent (element , new Node <>());
31
+ }
32
+
33
+ /**
34
+ * Unites the set that contains left and the set that contains right
35
+ * into a new set with the union-by-rank heuristic.
36
+ * <p>
37
+ * Rank is an upper bound on the height of node.
38
+ */
39
+ public void union (T left , T right ) {
40
+ checkNotNull (left , "element" );
41
+ checkNotNull (right , "element" );
42
+
43
+ Node <T > leftNode = nodeMap .get (left ),
44
+ rightNode = nodeMap .get (right );
45
+
46
+ if (leftNode == null ) {
47
+ throw new NoSuchElementException (left .toString ());
48
+ }
49
+
50
+ if (rightNode == null ) {
51
+ throw new NoSuchElementException (right .toString ());
52
+ }
53
+
54
+ Node <T > leftSet = findSet (leftNode ),
55
+ rightSet = findSet (rightNode );
56
+
57
+ if (leftSet == rightSet ) {
58
+ return ;
59
+ }
60
+
61
+ if (leftSet .rank < rightSet .rank ) {
62
+ leftSet .parent = rightSet ;
63
+ } else {
64
+ rightSet .parent = leftSet ;
65
+ if (leftSet .rank == rightSet .rank ) {
66
+ leftSet .rank ++;
67
+ }
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Query two elements whether contained in the same set.
73
+ */
74
+ public boolean isConnected (T left , T right ) {
75
+ if (left == null || right == null ) {
76
+ return false ;
77
+ }
78
+
79
+ Node <T > leftNode = nodeMap .get (left );
80
+ if (leftNode == null ) {
81
+ return false ;
82
+ }
83
+
84
+ Node <T > rightNode = nodeMap .get (right );
85
+ if (rightNode == null ) {
86
+ return false ;
87
+ }
88
+
89
+ if (leftNode == rightNode ) {
90
+ return true ;
91
+ }
92
+
93
+ return findSet (leftNode ) == findSet (rightNode );
94
+ }
95
+
96
+ public Collection <Set <T >> toSets () {
97
+ Map <Node <T >, Set <T >> setMap = new HashMap <>();
98
+ for (Map .Entry <T , Node <T >> entry : nodeMap .entrySet ()) {
99
+ setMap .computeIfAbsent (findSet (entry .getValue ()), k -> new HashSet <>())
100
+ .add (entry .getKey ());
101
+ }
102
+ return setMap .values ();
103
+ }
104
+
105
+ public void show () {
106
+ toSets ().forEach (System .out ::println );
107
+ }
108
+
109
+ /**
110
+ * Find the set that contains the node, actual is the first node of the set.
111
+ * <p>
112
+ * Backtracking with path compression.
113
+ */
114
+ private Node <T > findSet (Node <T > node ) {
115
+ if (node != node .parent ) {
116
+ node .parent = findSet (node .parent );
117
+ }
118
+ return node .parent ;
119
+ }
120
+
121
+ private static void checkNotNull (Object obj , String msg ) {
122
+ if (obj == null ) {
123
+ throw new NullPointerException (msg + " must be not null" );
124
+ }
125
+ }
126
+
127
+ static class Node <T > {
128
+ int rank ;
129
+ Node <T > parent ;
130
+
131
+ Node () {
132
+ parent = this ;
133
+ }
134
+ }
135
+
136
+ }
0 commit comments