The data structure of the non-intersecting set keeps a group of non-Intersecting dynamic sets S = {S1, S2 ,..., SK}, each set is identified by a representative, representing a member of the set.
If x represents an object, the following operations are supported for non-intersecting sets:
MAKE-SET (x): Creates a new SET with the unique member x. Because each set does not want to be handed in, x does not appear in other sets.
UNION (x, y): combines a set containing x and y into a new set.
FIND-SET (x): returns the SET containing x.
1. array representation of non-intersecting sets
Save the name of the set where each element is located in an array. In this way, the Find operation is a simple O (1) Search. To perform the Union (x, y) operation, assume that x is in equivalence class I and y is in Equivalence Class j,
Scan the entire array and change all I to j. The Union operation of a continuous N-1 takes a certain amount of time (N2. This field is unacceptable if there are many Union operations.
2. List representation of non-intersecting sets
Each set is represented by a linked list. The first object of the linked list is represented by its collection. Each object in the linked list contains a set member and a pointer to the next object,
And pointer to the Representative. Each linked list contains head and tail pointers. head points to the linked list, and tail points to the last object in the linked list.
Simple implementation of Union: Concatenates the linked list of x to the end of the linked list of y. For each object in the linked list where x was originally located, update its pointer to the Representative.
On average, each operation requires a period (N.
Weighted merge: Each table contains the length of the table, which always connects shorter tables to the end of the long table. In this way, m, MAKE-SET, UNION, and FIND-SET operations will take (m + nlgn) time.
class SetNode(object): def __init__(self,key): self.key=key self.next=None self.rep=Noneclass SetEntry(object): def __init__(self): self.head=None self.tail=None self.len=0class DisjSet(object): def __init__(self,node): self.setlist=[] def make_set(self,node): S=SetEntry() S.head=node S.tail=node S.len=1 node.rep=node self.setlist.append(S) def find(self,node): return node.rep def union(self,node_x,node_y): rep_x=node_x.rep rep_y=node_y.rep if rep_x!=rep_y: for s in self.setlist: if s.head==rep_x: set_x=s elif s.head==rep_y: set_y=s if set_x.len>=set_y.len: set_x.tail.next=rep_y node=rep_y while node is not None: node.rep=rep_x node=node.next set_x.tail=set_y.tail set_x.len=set_x.len+set_y.len self.setlist.remove(set_y) return rep_x else: set_y.tail.next=rep_x node=rep_x while node is not None: node.rep=rep_y node=node.next set_y.tail=set_x.tail set_y.len+=set_x.len self.setlist.remove(set_x) return rep_y
3. Non-Intersecting collection forest
A tree is used to represent a set. The root of the tree is used to represent the set. Each node in the tree contains element data and a pointer to the parent node. The pointer to the root node is null.
You can use arrays to represent the tree non-explicitly: each Member T [I] in the array represents the parent node of element I. If I is the root, take p [I] as 0 or-1.
If Union operations are performed randomly, the tree may become a degraded tree. There are several ways to avoid this situation.
3.1 smart merge algorithm
It always makes smaller trees a child tree of larger trees. Another method is to calculate the sum by height.
In this way, the depth of any node will not exceed logN, the running time of the Find operation is O (logN), and the O (MlogN) is consumed for M consecutive operations ).
When implemented, let each element of the array contain the negative value of its tree size.
class DisjSet(object): def __init__(self,size): self.list=[-1]*size def find(self,x): if self.list[x]<0: return x else: return self.find(self.list[x]) def union(self,x,y): set_x=self.find(x) set_y=self.find(y) if set_x!=set_y: if self.list[set_x]>self.list[set_y]: self.list[set_y]+=self.list[set_x] self.list[set_x]=set_y return set_y else: self.list[set_x]+=self.list[set_y] self.list[set_y]=set_x return set_x
3.2 path compression
Path compression is performed during a Find (X) operation. Each node from X to the root path changes its parent node to the root node.
Path compression is fully compatible with size-based computation, but not fully compatible with height-based computation. When the path is compressed, the height of each tree changes. You can estimate the storage height of each tree and use rank to represent it.
class DisjSet_with_rank(object): def __init__(self,size): self.list=[-1]*size def find(self,x): if self.list[x]<0: return x else: self.list[x]=self.find(self.list[x]) return self.list[x] def union(self,x,y): set_x=self.find(x) set_y=self.find(y) if set_x!=set_y: if self.list[set_x]<self.list[set_y]: self.list[set_y]=set_x else: if self.list[set_x]==self.list[set_y]: self.list[set_y]-=1 self.list[set_x]=set_y
Explicit Representation of path compression
class SetNode(object): def __init__(self,key): self.parent=None self.key=key self.rank=1def find(node): if node.parent is None: return node else: node.parent=find(node.parent) return node.parentdef union(x,y): x=find(x) y=find(y) if x!=y: if x.rank<=y.rank: if x.rank==y.rank: y.rank+=1 x.parent=y return y else: y.parent=x return x