Point:
When each element is regarded as a vertex, a simple compound is a undirected edge. If a ring exists (that is, K pairs have k elements in the combination), it is dangerous and should not be packed. Otherwise, packing;
Maintain the connected component set with a query set. Check whether X and Y are in the same set each time a compound (X, Y) is obtained. If yes, reject. Otherwise, accept.
Query set
The query set is a tree-type data structure used to deal with the merge and query problems of some disjoint sets. Forest is often used.
Set is to make each element constitute a single element set, that is, to merge the sets of elements belonging to the same group in a certain order.
Bytes ---------------------------------------------------------------------------------------------------------
From: http://www.cnblogs.com/cyjb/p/UnionFindSets.html,3Q ~
Bytes ---------------------------------------------------------------------------------------------------------
Union-find sets is a very delicate and practical data structure, which is mainly used to processNon-intersecting set. Some common uses include the Kruskal Algorithm for Finding connected subgraphs, the least spanning tree, and the least common ancestors (LCA.
When a dataset is used for parallel query, a dynamic set of non-Intersecting values exists first. Generally, an integer is used to represent an element in the set.
Each set may contain one or more elements, and an element in the set is selectedRepresentative. The specific elements contained in each set are not concerned, and the specific element selected as the representative is generally not concerned. We are concerned that for a given element, we can quickly find the set where the element is located (the representative) and the set where the two elements are merged. the time complexity of these operations isConstant level.
The basic operations of the query set are as follows:
- Makeset (s): Creates a new query set, which contains s single-element sets.
- Unionset (x, y): merges the set where element x and element y are located. The set where X and Y are located does not overlap.
- Find (x): Find the representative of the set where element x is located. This operation can also be used to determine whether two elements are in the same set. Just compare their representatives.
The implementation principle of the query set is also relatively simple. That is, the tree is used to represent the set. each node in the tree represents an element in the set, and the element corresponding to the root is the representative of the set, 1.
Figure 1 and query the Tree Representation of the Set
There are two vertices in the figure, which correspond to two sets respectively. The first set is {a, B, c, d}, which indicates that the element is a; the second set is {E, F, g}, which indicates that the element is EE.
The node of the tree indicates the elements in the Set, the pointer indicates the pointer to the parent node, and the pointer to the root node points to itself, indicating that it has no parent node. You can find the root node of the tree by following the parent node of each node, that is, the representative element of the set.
Now, it is easy to write makeset and find code. Assume that a long enough array is used to store Tree nodes (similar to the previously mentioned static linked list ), makeset constructs a forest of 2, where each element is a single element set, that is, the parent node is its own:
Figure 2 construct and check set Initialization
The corresponding code is as follows. The time complexity is 0 (n ):
| 123456 |
const int MAXSIZE = 500;int uset[MAXSIZE]; void makeSet(int size) { for(int i = 0;i < size;i++) uset[i] = i;} |
The next step is the find operation. If each query is performed along the parent node, the time complexity is the height of the tree, and it is impossible to reach the constant level. Here we need to apply a very simple and effective policy-path compression.
Path compression means that each node in the search path is directed to the root node at each search, as shown in 3.
Figure 3 path compression
I have prepared two versions of the find operation implementation: recursive version and non-recursive version. However, there is no obvious efficiency gap between the two versions, therefore, the specific use depends on your personal preferences.
| 12345678910 |
int find(int x) { if (x != uset[x]) uset[x] = find(uset[x]); return uset[x];}int find(int x) { int p = x, t; while (uset[p] != p) p = uset[p]; while (x != p) { t = uset[x]; uset[x] = p; x = t; } return x;} |
The final operation is unionset, and the merging of query sets is also very simple, that is, pointing the root of a set to the root of another set, as shown in 4.
Figure 4 merge query Sets
Here, we can also apply a simple heuristic policy-merge by rank. This method uses rank to indicate the upper bound of the tree height. When merging, it always points the roots with a small rank to the roots with a large rank. Simply put, a short tree is always added to a higher tree as a subtree. To save the rank, you need to use an array of the same length as the uset and initialize all elements to 0.
| 12345678 |
void unionSet(int x, int y) { if ((x = find(x)) == (y = find(y))) return; if (rank[x] > rank[y]) uset[y] = x; else { uset[x] = y; if (rank[x] == rank[y]) rank[y]++; }} |
The following is the complete code for merging and querying sets by rank. Only recursive find operations are included.
const
int
MAXSIZE = 500;
int
uset[MAXSIZE];
int
rank[MAXSIZE];
void
makeSet(
int
size) {
for
(
int
i = 0;i < size;i++) uset[i] = i;
for
(
int
i = 0;i < size;i++) rank[i] = 0;
}
int
find(
int
x) {
if
(x != uset[x]) uset[x] = find(uset[x]);
return
uset[x];
}
void
unionSet(
int
x,
int
y) {
if
((x = find(x)) == (y = find(y)))
return
;
if
(rank[x] > rank[y]) uset[y] = x;
else
{
uset[x] = y;
if
(rank[x] == rank[y]) rank[y]++;
}
}
In addition to merge by rank and query sets, a common strategy is to merge by the number of elements contained in the Set (or the number of nodes in the tree), which will contain the roots with fewer nodes, point to the root of a tree that contains many nodes. This policy is similar to the rank-based merge policy, which can also improve the running speed of the query set and save the Additional Rank array.
This check set has a slightly different definition, that is, if the uset value is a positive number, it indicates the parent node of the element (INDEX); if it is a negative number, it indicates that the element is the representative of the Set (that is, the root), and the opposite number of values is the number of elements in the set. The Code is as follows, which also contains recursive and non-recursive find operations:
const
int
MAXSIZE = 500;
int
uset[MAXSIZE];
void
makeSet(
int
size) {
for
(
int
i = 0;i < size;i++) uset[i] = -1;
}
int
find(
int
x) {
if
(uset[x] < 0)
return
x;
uset[x] = find(uset[x]);
return
uset[x];
}
int
find(
int
x) {
int
p = x, t;
while
(uset[p] >= 0) p = uset[p];
while
(x != p) {
t = uset[x];
uset[x] = p;
x = t;
}
return
x;
}
void
unionSet(
int
x,
int
y) {
if
((x = find(x)) == (y = find(y)))
return
;
if
(uset[x] < uset[y]) {
uset[x] += uset[y];
uset[y] = x;
}
else
{
uset[y] += uset[x];
uset[x] = y;
}
}
You can use-uset [find (x)] to obtain the number of elements in the set where element x is located.
The space complexity of the query set is O (n). Obviously, if the query set is merged by rank, more space is occupied. The find and unionset operations can be regarded as constant-level operations, or, accurately, perform M searches or merge operations in a query set containing n elements, in the worst case, the required time is O (M α (N). Here α is an inverse function of the Ackerman function, in a very large range (much larger than the estimated number of atoms in the observed universe 10 ^ 80), it can be considered not greater than 4. For more information about the time complexity analysis, see section 21.4 "Introduction to algorithms" for rank-based merge analysis with path compression.