Introduction:
A disjoint set class is a collection of elements that are merged into disjoint pieces. Element 22 is equivalent in the same collection, and the elements in different sets are not equal to the price.
1. Equivalence relationshipThe equivalence relationship must meet the following three properties:
(1): reflexive, for any element in the set S A,a r A; (R is a defined relationship, e.g. R is <=, >=, etc.)
(2) symmetry, a r b when and only if B r a
(3): transitive, if a R B and B r C, then a R c
2. Dynamic equivalence issuesThe equivalence class of element A in set S is a subset of the set S, which contains all elements that have an equivalence relation to a. So to determine if a is equivalent to B, you only need to verify whether A and B belong to the same equivalence class.
The find operation that returns the name of the collection of equivalent classes containing the given element. For example: Find (a) = = Find (b), then A and B are in the same set. To add an action, if you want to add a ~ b to a relationship, first determine whether A and B have a relationship (you can verify that they are in the same equivalence class through the find operation). If A and B are not in the same equivalence class, use the Seek and operate union, which merges the two equivalent classes containing A and b into an equivalence class. This algorithm is called the algorithm of seeking and finding the disjoint set. the algorithm is dynamic because, during the execution of the algorithm, the collection can be changed by the Union operation.
There are two scenarios for solving the dynamic equivalence problem, the first of which is to ensure that command find can be executed at a constant worst-case run time, while another scheme guarantees that the operation union can execute at a constant worst-case run time. However, two operations cannot be performed at the same time with constant worst case times.
The
first scenario: to make the find Operation fast, you can save the name of an equivalence class for each element in an array. Find is now a simple O (1) lookup. Set a union (a, a), and set a in equivalence Class I, B in the equivalence class J, at which point we scan the array and change all I to J. The time complexity of Union is O (N);
The
second scenario: put all the elements in the same equivalence class into a linked list, updating without searching the entire array. If we also track the size of each equivalence class and change the name of the smaller equivalent class to the name of the larger equivalent class when the union is executed.
3. Basic data StructureEach element can be thought of as a separate tree, the name of each collection is represented by the root, and an array can be used to implement the idea. All elements are represented by an array, each member of the array S[i] represents the father of element I, if I is the root, then s[i]=-1;
Vector<int> s;//Store the root node or parent node of each element
a Find (x) operation on element x is done by returning the root of the tree that contains the X.
Merge Operation Union (ROOT1, Root2), link the parent node of one tree to the root node of another tree to merge the two trees.
Here's a concrete example: initialization element collection: 0,1,2,3,4,5,6,7
The merge Operation Union (4,5), which links the parent node of one tree to the root node of another tree, merges two trees.
The merge Operation Union (6,7), which links the parent node of one tree to the root node of another tree, merges two trees.
4. Algorithm for dexterous seeking and solvingthe merge function in the above disjoint set class wants to be random, and it is merged by making the second tree a subtree of the first tree.
The
first method of improvement:A simple improvement is the use of arbitrary methods to break the present randomness, so that the smaller tree is always the subtree of the larger tree. This method is called by size.
if not by size, then as the merge progresses, the depth of some of the collection trees increases too (greater than logn), which means that the find operation has an O (logn) execution time. to implement the method of merging by size, you need to remember the size of each tree, so that each root element contains a negative value of the size of its tree. When merging, first check the size of the tree, the smaller tree becomes the subtree of the larger tree, the size of the new tree is two tree size and.
example steps for merging by size:
The
second method of improvement:
By height, it also guarantees that the depth of all trees is O (logn). We track the height of each tree rather than the size and perform the merging so that the shallow tree becomes the subtree of the deep tree. The height of the tree increases only when the tree is only two trees of equal depth (depth of the tree plus 1). In order to achieve by height, you need to remember the height of each tree, you can let each root element contain a negative value of its tree height. The height of the tree (the value of the root element minus 1) needs to be updated only if the merged two trees are of equal height.
Steps to merge by tree height:
5. Key member functions
Explicit disjsets (int numelements); ~disjsets () {} int find (int x) const;//looks for void unionsets (int root1, int root2);//merge void Unionsetsbysize ( int root1, int root2);//merge void unionsetsbyheight (int root1, int root2) by tree size;//by tree height merge void print ();// Outputs the elements in each disjoint collection class
6. Introduction of key member functions(1): Lookup operation find (int x) const, returns the root of the tree of the collection where the element resides.
/***************************************************************** Function Name: find (int x) const* function Description: Find element x in the name of the collection * parameter list: X is the element to find * return Result: Returns the collection of element x name ********************************************************** /int disjsets::find (int x) const{ if (S[x] < 0) return x; else return find (S[x]);}
(2): Arbitrarily merges unionsets (int root1, int root2) and merges Root2 tree as a subtree of root1.
/***************************************************************** Function Name: unionsets (int root1, int root2) * Function Description: Merge two sets * parameter list: ROOT1 represents the collection 1,root2 represents the collection the results are returned: void ************************************************** /void disjsets::unionsets (int root1, int root2) { S[root2] = root1;}
(3): Merges unionsetsbysize (int root1, int root2) by the size of the tree, making the smaller tree a subtree of the larger tree
/***************************************************************** Function Name: unionsetsbysize (int root1, int root2) * Function Description: Merges two collections by collection size, making smaller trees a subtree of larger trees * parameter list: ROOT1 represents the collection 1,root2 represents the collection the results are returned: void ************************** /void disjsets::unionsetsbysize (int root1, int root2) { if (S[root2] < s [ROOT1]) {//root2 Tree comparison large s[root2] + = s[root1];//Update tree size S[ROOT1] = root2;//root1 parent node becomes Root2 } else{ s[root1 ] + = S[root2]; S[root2] = ROOT1; }}
(4): Merge two sets unionsetsbyheight (int root1, int root2) by tree height to make the shallow tree a subtree of the darker tree
/***************************************************************** Function Name: unionsetsbyheight (int root1, int ROOT2) * function Description: Merges two sets by the set height, making the shallower tree a subtree of the darker tree * parameter list: ROOT1 represents the collection 1,root2 represents the collection the results are returned: void ******************** /void disjsets::unionsetsbyheight (int root1, int root2) { if (s[ ROOT2] < s[root1]) {//root2 tree is higher s[root1] = root2;//is merged directly, ROOT1 becomes Root2 tree subtree } else{//root1 tree is higher, or equal. //If equal updates the height of the tree if (s[root1] = = S[root2]) s[root1]--; S[root2] = root1; } }
7. The following is the test main function main ()
Test main function int main () { cout << "arbitrarily merge:" << Endl; Disjsets Disjsets (8); Disjsets.unionsets (4, 5); Disjsets.unionsets (6, 7); Disjsets.unionsets (4, 6); Disjsets.print (); cout << "Merge by Size:" << Endl; Disjsets disjSets2 (8); Disjsets2.unionsetsbysize (4, 5); Disjsets2.unionsetsbysize (6, 7); Disjsets2.unionsetsbysize (4, 6); Disjsets2.unionsetsbysize (3, 4); Disjsets2.print (); cout << "Merge by height:" << endl; Disjsets DisjSets3 (8); Disjsets3.unionsetsbyheight (4, 5); Disjsets3.unionsetsbyheight (6, 7); Disjsets3.unionsetsbyheight (4, 6); Disjsets3.unionsetsbyheight (3, 4); Disjsets3.print (); return 0;}
8. The following is the source code for the disjoint set class
/*************************************************************************> File name:disjointsets.cpp> Author: > Mail: > Created time:2016 April 25 Monday 11:22 48 seconds ******************************************************* /#include <iostream> #include <vector>using namespace std;/*************************** Class Name: Disjoint collection class Disjsets********************************************/class disjsets{public:exp Licit disjsets (int numelements); ~disjsets () {} int find (int x) const;//find void unionsets (int root1, int root2);//merge void Union setsbysize (int root1, int root2);//merge void Unionsetsbyheight by tree size (int root1, int root2);//merge void print by tree height () ;//Output each disjoint collection class element private:void print (int x); Private:vector<int> s;//stores the root node or parent node of each element};/**************************************************************** * Function Name: disjsets (int numelements) * Function Description: constructor, set initialization of each element * parameter list:Numelements is the number of elements in the collection * return result: no *****************************************************************/disjsets::D isjsets (int numelements): s (numelements) {for (unsigned i = 0; i < s.size (); ++i) s[i] = 1;} /***************************************************************** function Name: print (int x) * Function Description: Print element x* parameter list: X is the element's * return Result: void *****************************************************************/void disjsets::p rint (int x) {cout < < x << ""; for (unsigned i = 0; i < s.size (); ++i) {if (s[i] = = x) print (i); }}/***************************************************************** function Name: print* function Description: Print elements in the collection * parameter list: none * return Result: Voi D *****************************************************************/void disjsets::p rint () {cout << output disjoint collection class (each line represents an intersection set): "<< Endl; cout << "s:"; for (unsigned i = 0; i < s.size (); ++i) cout << s[i] << ""; cout << Endl; for (unsigned i = 0; i < s.size(); ++i) {if (S[i] < 0) {print (i); cout << Endl; }}}/***************************************************************** Function Name: find (int x) const* function Description: Finds element x is in the name of the collection * Parameter list: X is the element to find * Returns the result: Returns the collection of element x First name *****************************************************************/int disjsets:: Find (int x) const{if (S[x] < 0) return x; else return find (S[x]);} /***************************************************************** function Name: unionsets (int root1, int root2) * Function Description: Merging two sets * parameter list: ROOT1 represents the collection 1,root2 represents the collection The results are returned: void *****************************************************************/ void disjsets::unionsets (int root1, int root2) {S[root2] = root1;} /***************************************************************** function Name: unionsetsbysize (int root1, int root2) * Function description : Merges two collections by collection size, making the smaller tree a subtree of the larger tree * parameter list: ROOT1 represents collection 1,root2 represents the collection The results are returned: void ********************************************** /void disjsets::unionsetsbysize (int root1, int root2) {if (S[root2] < s[root1]) {//root2 tree comparison large S[root2] + = s[root1];//Update tree size S[r OOT1] = root2;//root1 parent node becomes ROOT2} else{s[root1] + = S[root2]; S[root2] = ROOT1; }}/***************************************************************** function Name: unionsetsbyheight (int root1, int root2) * Function Description: Merges two sets by the set height, making the shallower tree a subtree of the darker tree * parameter list: ROOT1 represents the collection 1,root2 represents the collection The results are returned: void ****************************************** /void disjsets::unionsetsbyheight (int root1, int root2) {if (S[root2] < S[ROOT1]) {// Root2 trees higher s[root1] = root2;//are merged directly, ROOT1 become subtree of Root2 tree} else{//root1 tree is higher, or equal. If equal, update the height of the tree if (s[root1] = = S[root2]) s[root1]--; S[root2] = ROOT1; }}//test main function int main () {cout << "arbitrarily merge:" << Endl; Disjsets Disjsets (8); Disjsets.unionsets (4, 5); Disjsets.unionsets (6, 7); Disjsets.unionsets (4, 6); Disjsets.print (); cout << "Merge by Size:" << Endl; Disjsets disjSets2 (8); Disjsets2.unionsetsbysize (4, 5); Disjsets2.unionsetsbysize (6, 7); Disjsets2.unionsetsbysize (4, 6); Disjsets2.unionsetsbysize (3, 4); Disjsets2.print (); cout << "Merge by height:" << endl; Disjsets DisjSets3 (8); Disjsets3.unionsetsbyheight (4, 5); Disjsets3.unionsetsbyheight (6, 7); Disjsets3.unionsetsbyheight (4, 6); Disjsets3.unionsetsbyheight (3, 4); Disjsets3.print (); return 0;}
The result of the operation is:
Data structure and algorithm--C + + implementation of disjoint set class