-
Notifications
You must be signed in to change notification settings - Fork 16
/
DisjointSets.cs
257 lines (237 loc) · 7.98 KB
/
DisjointSets.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
using System;
using System.Collections.Generic;
using System.Linq;
namespace mapf;
public class DisjointSetItem
{
public DisjointSetItem parent;
public int rank;
public DisjointSetItem()
{
parent = this;
rank = 0;
}
public bool IsSingle()
{
if (this.parent != this)
return false;
if (this.rank != 0) // Rank is only correct in representative items!
return false;
return true;
}
}
/// <summary>
/// Algorithm from http://en.wikipedia.org/wiki/Disjoint-set_data_structure.
/// Operations cost O(Ackerman^-1(n)) time - practically constant. Cost is asymptotically optimal :)
/// </summary>
public class DisjointSets<T>
{
protected IDictionary<T, DisjointSetItem> entriesToItems;
public int maxRank { get; protected set; }
public DisjointSets()
{
entriesToItems = new Dictionary<T, DisjointSetItem>();
maxRank = 0;
}
/// <summary>
/// Initially all entries are single item sets.
/// </summary>
/// <param name="entries"></param>
public DisjointSets(ISet<T> entries)
{
entriesToItems = new Dictionary<T, DisjointSetItem>();
maxRank = 0;
foreach (var entry in entries)
{
entriesToItems[entry] = new DisjointSetItem();
}
}
public DisjointSets(DisjointSets<T> other)
{
entriesToItems = new Dictionary<T, DisjointSetItem>();
maxRank = other.maxRank;
var queue = new Queue<T>(other.entriesToItems.Keys);
var otherRepsToNewReps = new Dictionary<DisjointSetItem, DisjointSetItem>();
while (queue.Count != 0)
{
var entry = queue.Dequeue();
var item = other.entriesToItems[entry];
other.Find(item); // Makes item.parent point directly do the set's representative
if (item.parent == item) // This is the set's representative
{
var newItem = new DisjointSetItem();
entriesToItems[entry] = newItem;
// .parent already points to itself as needed
newItem.rank = item.rank; // Since all items in a set do not point directly to the set's rep,
// the set's rank would have been 1 regardless of its size, had it
// been created by calls to Union. We prefer to remember the original rank.
otherRepsToNewReps[item] = newItem;
}
else
{
if (otherRepsToNewReps.ContainsKey(item.parent)) // We've copied this rep already
{
var newItem = new DisjointSetItem();
entriesToItems[entry] = newItem;
newItem.parent = otherRepsToNewReps[item.parent];
// .rank not important in non-rep items
}
else
queue.Enqueue(entry); // Wait for the rep to be copied first.
}
}
}
/// <summary>
/// Does nothing if entry is already present.
/// Returns whether the entry was really added now.
/// </summary>
public bool AddSet(T entry)
{
if (entriesToItems.ContainsKey(entry) == false)
{
entriesToItems[entry] = new DisjointSetItem();
return true;
}
return false;
}
/// <summary>
/// Assumes entry is already in the DisjointSets data structure
/// </summary>
/// <param name="entry"></param>
/// <returns></returns>
public bool IsSingle(T entry)
{
var item = entriesToItems[entry];
return item.IsSingle();
}
public IEnumerable<ISet<T>> GetSets()
{
Dictionary<DisjointSetItem, ISet<T>> repsToSets = new Dictionary<DisjointSetItem, ISet<T>>();
foreach (var entryAndSetItem in entriesToItems)
{
var entry = entryAndSetItem.Key;
var item = entryAndSetItem.Value;
var rep = Find(item);
if (repsToSets.ContainsKey(rep) == false)
repsToSets.Add(rep, new HashSet<T>());
repsToSets[rep].Add(entry);
}
return repsToSets.Values;
}
public int GetNumOfSets()
{
int count = 0;
foreach (var entryAndSetItem in entriesToItems)
{
var entry = entryAndSetItem.Key;
var item = entryAndSetItem.Value;
if (item.parent == item)
count += 1;
}
return count;
}
public override string ToString()
{
string ans = "{";
foreach (var set in GetSets())
{
ans += "{";
foreach (var item in set)
{
ans += item.ToString() + ", ";
}
ans += "}, ";
}
ans += "}";
return ans;
}
/// <summary>
/// Modifies this data structure to also contains all unions from other.
/// </summary>
/// <param name="other"></param>
/// <returns>Whether this object was changed</returns>
public bool CopyUnions(DisjointSets<T> other)
{
// Create a temporary reverse mapping of other's items to its entries - O(n)
var otherItemsToEntries = new Dictionary<DisjointSetItem, T>();
foreach (var kvp in other.entriesToItems)
{
otherItemsToEntries[kvp.Value] = kvp.Key;
}
// Unite each of other's entries with its representative only - O(n)
bool ret = false;
foreach (var entryAndSetItem in other.entriesToItems)
{
var entry = entryAndSetItem.Key;
var item = entryAndSetItem.Value;
if (item.IsSingle() == true)
continue;
other.Find(item);
var repEntry = otherItemsToEntries[item.parent];
bool wasOnlyUnitedNow = this.Union(entry, repEntry);
ret = ret || wasOnlyUnitedNow;
}
return ret;
}
/// <summary>
/// Finds the representative of the item's set
/// </summary>
/// <param name="x"></param>
/// <returns></returns>
protected DisjointSetItem Find(DisjointSetItem x)
{
if (x.parent != x)
x.parent = Find(x.parent);
return x.parent;
}
/// <summary>
/// Doesn't assume x, y already in the data structure.
/// Returns whether they were really only united now.
/// </summary>
/// <param name="entryA"></param>
/// <param name="entryB"></param>
/// <returns></returns>
public bool Union(T entryA, T entryB)
{
AddSet(entryA);
AddSet(entryB);
var x = entriesToItems[entryA];
var y = entriesToItems[entryB];
var xRoot = Find(x);
var yRoot = Find(y);
if (xRoot == yRoot)
return false;
// x and y are not already in same set. Merge them.
if (xRoot.rank < yRoot.rank)
xRoot.parent = yRoot;
else if (xRoot.rank > yRoot.rank)
yRoot.parent = xRoot;
else
{
// Arbitrarily choose x to be the root of the union
yRoot.parent = xRoot;
xRoot.rank = xRoot.rank + 1;
if (xRoot.rank > maxRank)
maxRank = xRoot.rank;
}
return true;
}
/// <summary>
/// Assumes entries are already in the DisjointSets data structure
/// </summary>
/// <param name="entryA"></param>
/// <param name="entryB"></param>
/// <returns></returns>
public bool AreUnited(T entryA, T entryB)
{
var x = entriesToItems[entryA];
var y = entriesToItems[entryB];
var xRoot = Find(x);
var yRoot = Find(y);
return xRoot == yRoot;
}
public bool Contains(T entry)
{
return entriesToItems.ContainsKey(entry);
}
}