Lec 7 Implementasi Disjoint Set
Making a “good” maze
Disjoint Set Union/Find ADT
Up-trees
Maze revisited
Represent maze environment as graph {V,E} collection of rooms: V
connections between rooms (initially all closed):
E
Construct a maze: collection of rooms: V = V
designated rooms in, iV, and out, oV
collection of connections to knock down: E E such that a unique path connects any two rooms
So far, some walls have been knocked down while others remain.
Now, we consider the wall between A and B.
A
Should we knock it down? no, if A and B are otherwise connected
B yes, if A and B are not otherwise connected While edges remain in E
Remove a random edge e = (u, v) from E
If u and v have not yet been connected
e to E
- add
u and v as connected
- mark
Connection between rooms is an equivalence relation (call it C)
For any rooms a, b, c a C a (A room connects to itself!) If a C b, then b C a If a C b and b C c, then a C cOther equivalence relations? Union/Find ADT operations union find( 4 )
{1,4,8} {6} fnd
create
8
{7}
{2,3,6} destroy
{5,9,10}
{2,3} union(
2 ,
6 )Disjoint set equivalence property: every element of a DS U/F structure belongs to exactly one set Dynamic equivalence property: the set of an element can change after execution of a union
Given a set U = {a 1 , a 2 , … , a n } Maintain a partition of U, a set of subsets of U {S 1 , S 2 , … , S k } such that: -
- - each pair of subsets S i and S j are disjoint: together, the subsets cover U:
- - The S i
are mutually exclusive and collectively exhaustive Union(a, b) creates a new subset which is the union of a’s subset and b’s subset
NOTE: outside agent decides when/what to union - ADT is just the
bookkeeperFind(a) returns a unique name for a’s subset NOTE: set names are arbitrary! We only care that:
k i i S U 1
j i S S
3
10
a b c
Construct the maze on the right
2
1
6 Initial state (set names underlined):
4
7
d e f
{a}{b}{c}{d}{e}{f}{g}{h}{i}
11
9
8 Maze constructor (outside agent) traverses edges in random order
12
5 and decides whether to union
g h i
{a}{b}{c}{d}{e}{f}{g}{h}
3
10
a b c
{i}
2
1
6 find(b) b find(e) e
4
7
d e f
find(b) find(e) so: add
1 to E
11
9
8 union(b, e)
12
5
g h i
{a}{b,e}{c}{d}{f}{g}{h} {i}
Order of edges in blue
Finding the representative member of a set
is somewhat like the opposite of finding whether a given item exists in a set.So, instead of using trees with pointers from each node to its children; let’s use trees with a pointer from each node to its parent.
Each subset is an up- tree with its root as its representative member
a c h
g
All members of a given set are nodes in that set’s up-tree
f i d b
Hash table maps input data to the node associated with that
e
data Up-trees are not necessarily binary! find( f ) find( e )
a b c
10
a c g h
d e f
7
f i
d b
11
9
8 g h i
12 e
Just traverse to the root! union( a,c )
a b c
10 a c g h d e f
f i d b
11
9
8 g h i
12
e
Just hang one root from the other! runtime: a b c
3
10
2
1
6 d e f
4
7
11
9
8
union( b , e )
g h i
12
5
a b c d e f g h i a b c d f g h i a b c
3
10
2
6 d e f
4
7
11
9
8
union( a , d )
g h i
12
5 a b c d f g h i
e a b c f g h i a b c
3
10
6 d e f
4
7
11
9
8
union( a , b )
g h i
12
5 a b c f g h i
d e a c f g h i b d a b c
10
6 d e f
4
7
11
9
8
find( d ) = find( e )
g h i
12
5 No union!
a c f g h i b d
e a b c
10
6 d e f
7
11
9
8
union( h , i )
g h i
12
5
a c f g h i a c f g h b d i b d a b c
10
6 d e f
7
11
9
8
union( c , f )
g h i
12
a c f g h a c g h b d b d f i i a b c
10 d e f
7
find( e )
11
9
8
find( f ) union( a , c )
g h i
12 a c g h
c g h b d f a f i i b d e a d b e c f g h i
11
10
9
8
12
f g h a b c i d f g h a b
c
i d find( f ) find( i ) union( c , h ) find( e ) = find( h ) and find( b ) = find( c ) So, no unions for either of these.
a d b e c f g h i
11
10
9
12
f g h a b
c
i d a b c d e f
find( d )
11
find( g )
g h i
union( c , g )
12 c g
g c a f h a f h b d i b d i find( g ) = find( h ) So, no union.
And, we’re done!
a d b e c f g h i
12
f g h a b c i d
a d b e c f g h i
Ooh… scary!
A forest of up-trees can easily be
a c g h stored in an array.
Also, if the node
b d f i
names are integers or characters, we can use a very
e
simple, perfect Nifty storage trick! hash.
0 (a) 1 (b) 2 (c) 3 (d) 4 (e) 5 (f) 6 (g) 7 (h) 8 (i)
up-index: -1 -1
1 2 -1 -1
7
typedef ID int;
ID union(ID x, ID y) {
ID find(Object x) { assert(up[x] == -1); assert(hTable.contains(x)); assert(up[y] == -1);
ID xID = hTable[x]; up[y] = x; while(up[xID] != -1) { } xID = up[xID]; } return xID; } runtime: O(depth) or … runtime: O(1)
Simple ADT, simple data structure, simple code
Complex complexity analysis, but extremely useful
result: essentially, constant time! Lots of potential applications Object-property collections Index partitions: e.g. parts inspection To say nothing of maze construction
In some applications, it may make sense to have meaningful (non-arbitrary) set names