Disjoint-set data structure

A disjoint-set data structure (sometimes called union-find data structure) is a data structure that keeps track of a partitioning of a set into a number of separate, nonoverlapping sets. It performs two operations :

  • Find: Determine which set a particular element is in.
  • Union: Combine or merge two sets into a single set.

REFERENCES:

AUTHORS:

  • Sebastien Labbe (2008) - Initial version.
  • Sebastien Labbe (2009-11-24) - Pickling support
  • Sebastien Labbe (2010-01) - Inclusion into sage (#6775).

EXAMPLES:

Disjoint set of integers from 0 to n-1:

sage: s = DisjointSet(6)
sage: s
{{0}, {1}, {2}, {3}, {4}, {5}}
sage: s.union(2, 4)
sage: s.union(1, 3)
sage: s.union(5, 1)
sage: s
{{0}, {1, 3, 5}, {2, 4}}
sage: s.find(3)
1
sage: s.find(5)
1
sage: map(s.find, range(6))
[0, 1, 2, 1, 2, 1]

Disjoint set of hashables objects:

sage: d = DisjointSet('abcde')
sage: d
{{'a'}, {'b'}, {'c'}, {'d'}, {'e'}}
sage: d.union('a','b')
sage: d.union('b','c')
sage: d.union('c','d')
sage: d
{{'a', 'b', 'c', 'd'}, {'e'}}
sage: d.find('c')
'a'
sage.sets.disjoint_set.DisjointSet(arg)

Construction of the DisjointSet where each element of arg is in its own set. If arg is an integer, then the DisjointSet returned is made of the integers from 0 to arg-1.

INPUT:

  • arg - non negative integer or an iterable of hashable objects.

EXAMPLES:

From a non-negative integer:

sage: DisjointSet(5)  
{{0}, {1}, {2}, {3}, {4}}

From an iterable:

sage: DisjointSet('abcde')  
{{'a'}, {'b'}, {'c'}, {'d'}, {'e'}}
sage: DisjointSet(range(6))
{{0}, {1}, {2}, {3}, {4}, {5}}
sage: DisjointSet(['yi',45,'cheval'])
{{'cheval'}, {'yi'}, {45}}

TESTS:

sage: DisjointSet(0)
{}
sage: DisjointSet('')
{}
sage: DisjointSet([])
{}

The argument must be a non negative integer:

sage: DisjointSet(-1)
...
ValueError: arg (=-1) must be a non negative integer

or an iterable:

sage: DisjointSet(4.3)
...
TypeError: 'sage.rings.real_mpfr.RealLiteral' object is not iterable

Moreover, the iterable must consist of hashable:

sage: DisjointSet([{}, {}])
...
TypeError: unhashable type: 'dict'
class sage.sets.disjoint_set.DisjointSet_class

Bases: sage.structure.sage_object.SageObject

Common class and methods for DisjointSet_of_integers and DisjointSet_of_hashables.

class sage.sets.disjoint_set.DisjointSet_of_hashables

Bases: sage.sets.disjoint_set.DisjointSet_class

Disjoint set of hashables.

EXAMPLES:

sage: d = DisjointSet('abcde')
sage: d
{{'a'}, {'b'}, {'c'}, {'d'}, {'e'}}
sage: d.union('a', 'c')
sage: d
{{'a', 'c'}, {'b'}, {'d'}, {'e'}}
sage: d.find('a')
'a'

TESTS:

sage: a = DisjointSet('abcdef')
sage: a == loads(dumps(a))
True
sage: a.union('a','c')
sage: a == loads(dumps(a))
True
cardinality()

Returns the number of elements in the disjoint set.

EXAMPLES:

sage: d = DisjointSet(range(5))
sage: d.cardinality()
5
sage: d.union(2, 4)
sage: d.cardinality()
5
element_to_root_dict()

Returns the dictionary where the keys are the elements of self and the values are their representative inside a list.

EXAMPLES:

sage: d = DisjointSet(range(5))
sage: d.union(2,3)
sage: d.union(4,1)
sage: e = d.element_to_root_dict(); e
{0: 0, 1: 4, 2: 2, 3: 2, 4: 4}
sage: print WordMorphism(e)
WordMorphism: 0->0, 1->4, 2->2, 3->2, 4->4
find(e)

Returns the representative of the set that e currently belongs to.

INPUT:

  • e - element in self

EXAMPLES:

sage: e = DisjointSet(range(5))
sage: e.union(4,2)
sage: e
{{0}, {1}, {2, 4}, {3}}
sage: e.find(2)
4
sage: e.find(4)
4
sage: e.union(1,3)
sage: e
{{0}, {1, 3}, {2, 4}}
sage: e.find(1)
1
sage: e.find(3)
1
sage: e.union(3,2)
sage: e
{{0}, {1, 2, 3, 4}}
sage: [e.find(i) for i in range(5)]
[0, 1, 1, 1, 1]
sage: e.find(5)
...
KeyError: 5
root_to_elements_dict()

Returns the dictionary where the keys are the roots of self and the values are the elements in the same set.

EXAMPLES:

sage: d = DisjointSet(range(5))
sage: d.union(2,3)
sage: d.union(4,1)
sage: e = d.root_to_elements_dict(); e
{0: [0], 2: [2, 3], 4: [1, 4]}
union(e, f)

Combine the set of e and the set of f into one.

All elements in those two sets will share the same representative that can be gotten using find.

INPUT:

  • e - element in self
  • f - element in self

EXAMPLES:

sage: e = DisjointSet('abcde')
sage: e
{{'a'}, {'b'}, {'c'}, {'d'}, {'e'}}
sage: e.union('a','b')
sage: e
{{'a', 'b'}, {'c'}, {'d'}, {'e'}}
sage: e.union('c','e')
sage: e                 
{{'a', 'b'}, {'c', 'e'}, {'d'}}
sage: e.union('b','e')
sage: e
{{'a', 'b', 'c', 'e'}, {'d'}}
class sage.sets.disjoint_set.DisjointSet_of_integers

Bases: sage.sets.disjoint_set.DisjointSet_class

Disjoint set of integers from 0 to n-1.

EXAMPLES:

sage: d = DisjointSet(5)
sage: d
{{0}, {1}, {2}, {3}, {4}}
sage: d.union(2,4)
sage: d.union(0,2)
sage: d
{{0, 2, 4}, {1}, {3}}
sage: d.find(2)
2

TESTS:

sage: a = DisjointSet(5)
sage: a == loads(dumps(a))
True
sage: a.union(3,4)
sage: a == loads(dumps(a))
True
cardinality()

Returns the number of elements in the disjoint set.

EXAMPLES:

sage: d = DisjointSet(5)
sage: d.cardinality()
5
sage: d.union(2, 4)
sage: d.cardinality()
5
element_to_root_dict()

Returns the dictionary where the keys are the elements of self and the values are their representative inside a list.

EXAMPLES:

sage: d = DisjointSet(5)
sage: d.union(2,3)
sage: d.union(4,1)
sage: e = d.element_to_root_dict(); e
{0: 0, 1: 4, 2: 2, 3: 2, 4: 4}
sage: print WordMorphism(e)
WordMorphism: 0->0, 1->4, 2->2, 3->2, 4->4
find(i)

Returns the representative of the set that i currently belongs to.

INPUT:

  • i - element in self

EXAMPLES:

sage: e = DisjointSet(5)
sage: e.union(4,2)
sage: e
{{0}, {1}, {2, 4}, {3}}
sage: e.find(2)
4
sage: e.find(4)
4
sage: e.union(1,3)
sage: e
{{0}, {1, 3}, {2, 4}}
sage: e.find(1)
1
sage: e.find(3)
1
sage: e.union(3,2)
sage: e
{{0}, {1, 2, 3, 4}}
sage: [e.find(i) for i in range(5)]
[0, 1, 1, 1, 1]
sage: e.find(5)
...
ValueError: i(=5) must be between 0 and 4
root_to_elements_dict()

Returns the dictionary where the keys are the roots of self and the values are the elements in the same set as the root.

EXAMPLES:

sage: d = DisjointSet(5)
sage: d.root_to_elements_dict()
{0: [0], 1: [1], 2: [2], 3: [3], 4: [4]}
sage: d.union(2,3)
sage: d.root_to_elements_dict()
{0: [0], 1: [1], 2: [2, 3], 4: [4]}
sage: d.union(3,0)
sage: d.root_to_elements_dict()
{1: [1], 2: [0, 2, 3], 4: [4]}
sage: d
{{0, 2, 3}, {1}, {4}}
to_digraph()

Returns the current digraph of self where (a,b) is an oriented edge if b is the parent of a.

EXAMPLES:

sage: d = DisjointSet(5)
sage: d.union(2,3)
sage: d.union(4,1)
sage: d.union(3,4)
sage: d
{{0}, {1, 2, 3, 4}}
sage: g = d.to_digraph(); g
Looped digraph on 5 vertices
sage: g.edges()
[(0, 0, None), (1, 2, None), (2, 2, None), (3, 2, None), (4, 2, None)]

The result depends on the ordering of the union:

sage: d = DisjointSet(5)
sage: d.union(1,2)
sage: d.union(1,3)
sage: d.union(1,4)
sage: d
{{0}, {1, 2, 3, 4}}
sage: d.to_digraph().edges()
[(0, 0, None), (1, 1, None), (2, 1, None), (3, 1, None), (4, 1, None)]
union(i, j)

Combine the set of i and the set of j into one.

All elements in those two sets will share the same representative that can be gotten using find.

INPUT:

  • i - element in self
  • j - element in self

EXAMPLES:

sage: d = DisjointSet(5)
sage: d
{{0}, {1}, {2}, {3}, {4}}
sage: d.union(0,1)
sage: d
{{0, 1}, {2}, {3}, {4}}
sage: d.union(2,4)
sage: d
{{0, 1}, {2, 4}, {3}}
sage: d.union(1,4)
sage: d
{{0, 1, 2, 4}, {3}}
sage: d.union(1,5)
...
ValueError: j(=5) must be between 0 and 4
sage.sets.disjoint_set.OP_represent(n, merges, perm)

Demonstration and testing.

TESTS:

sage: from sage.groups.perm_gps.partn_ref.automorphism_group_canonical_label import OP_represent
sage: OP_represent(9, [(0,1),(2,3),(3,4)], [1,2,0,4,3,6,7,5,8])
Allocating OrbitPartition...
Allocation passed.
Checking that each element reports itself as its root.
Each element reports itself as its root.
Merging:
Merged 0 and 1.
Merged 2 and 3.
Merged 3 and 4.
Done merging.
Finding:
0 -> 0, root: size=2, mcr=0, rank=1
1 -> 0
2 -> 2, root: size=3, mcr=2, rank=1
3 -> 2
4 -> 2
5 -> 5, root: size=1, mcr=5, rank=0
6 -> 6, root: size=1, mcr=6, rank=0
7 -> 7, root: size=1, mcr=7, rank=0
8 -> 8, root: size=1, mcr=8, rank=0
Allocating array to test merge_perm.
Allocation passed.
Merging permutation: [1, 2, 0, 4, 3, 6, 7, 5, 8]
Done merging.
Finding:
0 -> 0, root: size=5, mcr=0, rank=2
1 -> 0
2 -> 0
3 -> 0
4 -> 0
5 -> 5, root: size=3, mcr=5, rank=1
6 -> 5
7 -> 5
8 -> 8, root: size=1, mcr=8, rank=0
Deallocating OrbitPartition.
Done.
sage.sets.disjoint_set.PS_represent(partition, splits)

Demonstration and testing.

TESTS:

sage: from sage.groups.perm_gps.partn_ref.automorphism_group_canonical_label import PS_represent
sage: PS_represent([[6],[3,4,8,7],[1,9,5],[0,2]], [6,1,8,2])
Allocating PartitionStack...
Allocation passed:
(0 1 2 3 4 5 6 7 8 9)
Checking that entries are in order and correct level.
Everything seems in order, deallocating.
Deallocated.
Creating PartitionStack from partition [[6], [3, 4, 8, 7], [1, 9, 5], [0, 2]].
PartitionStack's data:
entries -> [6, 3, 4, 8, 7, 1, 9, 5, 0, 2]
levels -> [0, 10, 10, 10, 0, 10, 10, 0, 10, -1]
depth = 0, degree = 10
(6|3 4 8 7|1 9 5|0 2)
Checking PS_is_discrete:
False
Checking PS_num_cells:
4
Checking PS_is_mcr, min cell reps are:
[6, 3, 1, 0]
Checking PS_is_fixed, fixed elements are:
[6]
Copying PartitionStack:
(6|3 4 8 7|1 9 5|0 2)
Checking for consistency.
Everything is consistent.
Clearing copy:
(0 3 4 8 7 1 9 5 6 2)
Splitting point 6 from original:
0
(6|3 4 8 7|1 9 5|0 2)
Splitting point 1 from original:
5
(6|3 4 8 7|1|5 9|0 2)
Splitting point 8 from original:
1
(6|8|3 4 7|1|5 9|0 2)
Splitting point 2 from original:
8
(6|8|3 4 7|1|5 9|2|0)
Getting permutation from PS2->PS:
[6, 1, 0, 8, 3, 9, 2, 7, 4, 5]
Finding first smallest:
Minimal element is 5, bitset is:
0000010001
Deallocating PartitionStacks.
Done.

Previous topic

Sets

Next topic

Disjoint union of enumerated sets

This Page