C3. Union-Find
Gabriele R¨oger
Universit¨at Basel
Union-Find
Fragen
Sind die roten Knoten verbunden?
Wie viele Zusammenhangskomponenten hat der Graph?
Union-Find-Datentyp
K¨onnen Frage mit Hilfe folgendem Datentyp beantworten:
1 class UnionFind:
2 # Initialisiert n Knoten mit Namen 0, ..., n-1
3 def __init__(n: int) -> None
4
5 # F¨ugt Verbindung zwischen v und w hinzu
6 def union(v: int, w: int) -> None
7
8 # Komponentenbezeichner f¨ur v
9 def find(v: int) -> int
10
11 # Sind v und w verbunden?
12 def connected(v: int, w: int) -> bool
13
14 # Anzahl der Zusammenhangskomponenten
15 def count() -> int
(Etwas) naiver Algorithmus: Quick-Find
F¨urn Knoten: Arrayid der L¨angen Eintrag an Stelle i ist Bezeichner der
Zusammenhangskomponente, in der Knoten i liegt.
Anf¨anglich liegt jeder Knoten (alleine) in seiner eigenen Zusammenhangskomponente (insgesamtn St¨uck).
Aktualisiere das Array bei jedem Aufruf vonunion.
(Etwas) naiver Algorithmus: Quick-Find
F¨urn Knoten: Arrayid der L¨angen Eintrag an Stelle i ist Bezeichner der
Zusammenhangskomponente, in der Knoten i liegt.
Anf¨anglich liegt jeder Knoten (alleine) in seiner eigenen Zusammenhangskomponente (insgesamtn St¨uck).
Aktualisiere das Array bei jedem Aufruf vonunion.
(Etwas) naiver Algorithmus: Quick-Find
F¨urn Knoten: Arrayid der L¨angen Eintrag an Stelle i ist Bezeichner der
Zusammenhangskomponente, in der Knoten i liegt.
Anf¨anglich liegt jeder Knoten (alleine) in seiner eigenen Zusammenhangskomponente (insgesamtn St¨uck).
Aktualisiere das Array bei jedem Aufruf vonunion.
Quick-Find-Algorithmus
1 class QuickFind:
2 def __init__(self, no_nodes):
3 self.id = list(range(no_nodes))
4 self.components = no_nodes
5
6 def find(self, v):
7 return self.id[v]
8
9 def union(self, v, w):
10 id_v = self.find(v)
11 id_w = self.find(w)
12 if id_v == id_w: # already in same component
13 return
14 # replace all occurrences of id_v in self.id with id_w
15 for i in range(len(self.id)):
16 if self.id[i] == id_v:
17 self.id[i] = id_w
18 self.components -= 1 # we merged two components
[0, 1, ..., no nodes-1]
Quick-Find-Algorithmus (Fortsetzung)
20 def connected(self, v, w):
21 return self.find(v) == self.find(w)
22
23 def count(self):
24 return self.components
Aufwand?
Kostenmodell = Anzahl Arrayzugriffe ein Arrayzugriff f¨ur jeden Aufruf vonfind zwischen n+ 3 und 2n+ 1 Arrayzugriffe
f¨ur jeden Aufruf von union, der zwei Komponenten vereinigt
Etwas besserer Algorithmus: Quick-Union
(implizite) Baumstruktur zur Repr¨asentation jeder Zusammenhangskomponente
Repr¨asentiert durch Array mit Eintrag des Elternknotens (Wurzel: Referenz auf sich selbst)
0 1 2 3 4 5 6 7 8
3 5 0 3 6 5 3 6 5 3
6
4 7
0
2 5
8 1
Wurzelknoten dient als Bezeichner der Zusammenhangskomponente
Etwas besserer Algorithmus: Quick-Union
(implizite) Baumstruktur zur Repr¨asentation jeder Zusammenhangskomponente
Repr¨asentiert durch Array mit Eintrag des Elternknotens (Wurzel: Referenz auf sich selbst)
0 1 2 3 4 5 6 7 8
3 5 0 3 6 5 3 6 5 3
6
4 7
0
2 5
8 1
Wurzelknoten dient als Bezeichner der Zusammenhangskomponente
Quick-Union-Algorithmus
1 class QuickUnion:
2 def __init__(self, no_nodes):
3 self.parent = list(range(no_nodes))
4 self.components = no_nodes
5
6 def find(self, v):
7 while self.parent[v] != v:
8 v = self.parent[v]
9 return v
10
11 def union(self, v, w):
12 id_v = self.find(v)
13 id_w = self.find(w)
14 if id_v == id_w: # already in same component
15 return
16 self.parent[id_v] = id_w
17 self.components -= 1
18
19 # connected und count wie bei QuickFind
Erste Verbesserung
Problem bei Quick-Union: B¨aume k¨onnen zu Ketten entarten
→ find ben¨otigt lineare Zeit in der Gr¨osse der Komponente.
Idee: H¨ange inunion flacheren Baum an Wurzel Idee: des tieferen Baums
Ranked-Quick-Union-Algorithmus
1 class RankedQuickUnion:
2 def __init__(self, no_nodes):
3 self.parent = list(range(no_nodes))
4 self.components = no_nodes
5 self.rank = [0] * no_nodes # [0, ..., 0]
6
7 def union(self, v, w):
8 id_v = self.find(v)
9 id_w = self.find(w)
10 if id_v == id_w:
11 return
12 if self.rank[id_w] < self.rank[id_v]:
13 self.parent[id_w] = id_v
14 else:
15 self.parent[id_v] = id_w
16 if self.rank[id_v]== self.rank[id_w]:
17 self.rank[id_w] += 1
18 self.components -= 1
19
20 # connected, count und find wie bei QuickUnion
Zweite Verbesserung
Pfadkompression
Idee: H¨ange infind alle traversierten Knoten direkt an die Wurzel um
Wir aktualisieren die H¨ohe des Baumes bei der Pfadkompression nicht.
Wert vonrankkann von tats¨achlicher H¨ohe abweichen.
Deshalb heisst er auchRang(rank) statt H¨ohe.
Ranked-Quick-Union-Algorithmus mit Pfadkompression
1 class RankedQuickUnionWithPathCompression:
2 def __init__(self, no_nodes):
3 self.parent = list(range(no_nodes))
4 self.components = no_nodes
5 self.rank = [0] * no_nodes # [0, ..., 0]
6
7 def find(self, v):
8 if self.parent[v] == v:
9 return v
10 root = self.find(self.parent[v])
11 self.parent[v] = root
12 return root
13
14 # connected, count und union wie bei RankedQuickUnion
Diskussion
Mit allen Verbesserungen erreichen wir beinahe konstante amortisierte Kosten f¨ur alle Operationen
Genauer: [Tarjan 1975]
mAufrufe vonfind beinObjekten (und h¨ochstensn−1 Aufrufe vonunion, die zwei Komponenten vereinigen) O(mα(m,n)) Arrayzugriffe
αist Umkehrfunktion einer Variante der Ackermann-Funktion In der Praxis istα(m,n)≤3.
Trotzdem: es kann keinen Union-Find-Algorithmus geben, der lineare Zeit garantieren kann.
(unter
”Cell-Probe“-Berechnungsmodell)
Diskussion
Mit allen Verbesserungen erreichen wir beinahe konstante amortisierte Kosten f¨ur alle Operationen
Genauer: [Tarjan 1975]
mAufrufe vonfind beinObjekten (und h¨ochstensn−1 Aufrufe vonunion, die zwei Komponenten vereinigen) O(mα(m,n))Arrayzugriffe
αist Umkehrfunktion einer Variante derAckermann-Funktion In der Praxis istα(m,n)≤3.
Trotzdem: es kann keinen Union-Find-Algorithmus geben, der lineare Zeit garantieren kann.
(unter
”Cell-Probe“-Berechnungsmodell)
Diskussion
Mit allen Verbesserungen erreichen wir beinahe konstante amortisierte Kosten f¨ur alle Operationen
Genauer: [Tarjan 1975]
mAufrufe vonfind beinObjekten (und h¨ochstensn−1 Aufrufe vonunion, die zwei Komponenten vereinigen) O(mα(m,n))Arrayzugriffe
αist Umkehrfunktion einer Variante derAckermann-Funktion In der Praxis istα(m,n)≤3.
Trotzdem: es kann keinen Union-Find-Algorithmus geben, der lineare Zeit garantieren kann.
(unter
”Cell-Probe“-Berechnungsmodell)
Vergleich mit explorationsbasiertem Verfahren
Kapitel C2: Algorithmus ConnectedComponents, der auf Graphenexplorationbasiert
Nach der Vorberechnung kosten Anfragen nur konstante Zeit.
In der Praxis ist Union-Find meist schneller, da der Graph f¨ur viele Zwecke nicht vollst¨andig aufgebaut werden muss.
Ist der Graph schon aufgebaut, kann Graphenexploration besser sein.
Weiterer Vorteil von Union-Find Online-Verfahren
problemloses Hinzuf¨ugen weiterer Kanten
Zusammenhangskomponenten und
Aquivalenzklassen ¨
Wiederholung: Zusammenhangskomponenten
UngerichteterGraph
Zwei Knoten u und v sind genau dann in der gleichen Zusammenhangskomponente, wenn es einen Pfad
zwischen u und v gibt (= Knotenu und v verbundensind).
0 1
2 3
4
5
6
7 8
9
Zusammenhangskomponenten: Eigenschaften
Die Zusammenhangskomponenten definieren eine Partition der Knoten:
Jeder Knoten ist in einer Zusammenhangskomponente.
Kein Knoten ist in mehr als einer Zusammenhangskomponente.
”ist verbunden mit“ ist Aquivalenzrelation¨
reflexiv:Jeder Knoten ist mit sich selbst verbunden.
symmetrisch:Ist umitv verbunden, dann istv mitu verbunden.
transitiv:Istumitv verbunden undv mitw verbunden, dann istumitw verbunden.
Union-Find Zusammenhangskomponenten und ¨Aquivalenzklassen
Partition allgemein
Definition (Partition)
EinePartition einer endlichen Menge M ist eine Menge P nicht-leerer Teilmengen vonM, so dass
jedes Element von M in einer Menge in P vorkommt:
S
S∈PS =M, und
die Mengen in P paarweise disjunkt sind:
S∩S0=∅f¨ur S,S0 ∈P mitS 6=S0. Die Mengen inP heissen Bl¨ocke.
M ={e1, . . . ,e5}
P1={{e1,e4},{e3},{e2,e5}}
P2={{e1,e4,e5},{e3}}
ist keine Partition vonM.
P3={{e1,e4,e5},{e3},{e2,e5}}
ist keine Partition von M.
P4={{e1},{e2},{e3},{e4},{e5}}
ist eine Partition von M.
Union-Find Zusammenhangskomponenten und ¨Aquivalenzklassen
Partition allgemein
Definition (Partition)
EinePartition einer endlichen Menge M ist eine Menge P nicht-leerer Teilmengen vonM, so dass
jedes Element von M in einer Menge in P vorkommt:
S
S∈PS =M, und
die Mengen in P paarweise disjunkt sind:
S∩S0=∅f¨ur S,S0 ∈P mitS 6=S0. Die Mengen inP heissen Bl¨ocke.
M ={e1, . . . ,e5}
P1={{e1,e4},{e3},{e2,e5}}ist eine Partition von M.
P2={{e1,e4,e5},{e3}}
P3={{e1,e4,e5},{e3},{e2,e5}}
ist keine Partition von M.
P4={{e1},{e2},{e3},{e4},{e5}}
ist eine Partition von M.
Union-Find Zusammenhangskomponenten und ¨Aquivalenzklassen
Partition allgemein
Definition (Partition)
EinePartition einer endlichen Menge M ist eine Menge P nicht-leerer Teilmengen vonM, so dass
jedes Element von M in einer Menge in P vorkommt:
S
S∈PS =M, und
die Mengen in P paarweise disjunkt sind:
S∩S0=∅f¨ur S,S0 ∈P mitS 6=S0. Die Mengen inP heissen Bl¨ocke.
M ={e1, . . . ,e5}
P1={{e1,e4},{e3},{e2,e5}}ist eine Partition von M.
P2={{e1,e4,e5},{e3}}ist keine Partition von M. P3={{e1,e4,e5},{e3},{e2,e5}}
P4={{e1},{e2},{e3},{e4},{e5}}
ist eine Partition von M.
Union-Find Zusammenhangskomponenten und ¨Aquivalenzklassen
Partition allgemein
Definition (Partition)
EinePartition einer endlichen Menge M ist eine Menge P nicht-leerer Teilmengen vonM, so dass
jedes Element von M in einer Menge in P vorkommt:
S
S∈PS =M, und
die Mengen in P paarweise disjunkt sind:
S∩S0=∅f¨ur S,S0 ∈P mitS 6=S0. Die Mengen inP heissen Bl¨ocke.
M ={e1, . . . ,e5}
P1={{e1,e4},{e3},{e2,e5}}ist eine Partition von M.
P2={{e1,e4,e5},{e3}}ist keine Partition von M.
P3={{e1,e4,e5},{e3},{e2,e5}} ist keine Partition von M. P4={{e1},{e2},{e3},{e4},{e5}}
Partition allgemein
Definition (Partition)
EinePartition einer endlichen Menge M ist eine Menge P nicht-leerer Teilmengen vonM, so dass
jedes Element von M in einer Menge in P vorkommt:
S
S∈PS =M, und
die Mengen in P paarweise disjunkt sind:
S∩S0=∅f¨ur S,S0 ∈P mitS 6=S0. Die Mengen inP heissen Bl¨ocke.
M ={e1, . . . ,e5}
P1={{e1,e4},{e3},{e2,e5}}ist eine Partition von M.
P2={{e1,e4,e5},{e3}}ist keine Partition von M.
P3={{e1,e4,e5},{e3},{e2,e5}} ist keine Partition von M. P4={{e1},{e2},{e3},{e4},{e5}} ist eine Partition vonM.
Aquivalenzrelation allgemein ¨
Definition (¨Aquivalenzrelation)
EineAquivalenzrelation¨ auf einer MengeM ist eine
symmetrische, transitive und reflexiveRelationR ⊆M×M.
Wir schreibena∼b f¨ur (a,b)∈R und sagenaist ¨aquivalent zub.
symmetrisch: a∼b impliziertb ∼a transitiv: a∼b und b∼c implizierta∼c reflexiv: f¨ur alle e ∈M:e ∼e
Aquivalenzklassen ¨
Definition (¨Aquivalenzklassen)
SeiR eine ¨Aquivalenzrelation auf der Menge M. DieAquivalenzklasse¨ von a∈M ist die Menge
[a] ={b∈M |a∼b}.
Die Menge aller ¨Aquivalenzklassen ist eine Partition vonM. Umgekehrt:
F¨ur PartitionP definiere R={(x,y)| ∃B∈P :x,y∈B}
(alsox ∼y genau dann, wennx und y im gleichen Block).
Dann ist R eine ¨Aquivalenzrelation.
K¨onnen Partitionen als ¨Aquivalenzklassen betrachten und umgekehrt.
Aquivalenzklassen ¨
Definition (¨Aquivalenzklassen)
SeiR eine ¨Aquivalenzrelation auf der Menge M. DieAquivalenzklasse¨ von a∈M ist die Menge
[a] ={b∈M |a∼b}.
Die Menge aller ¨Aquivalenzklassen ist eine Partition vonM. Umgekehrt:
F¨ur PartitionP definiere R={(x,y)| ∃B∈P :x,y∈B}
(alsox ∼y genau dann, wennx und y im gleichen Block).
Dann ist R eine ¨Aquivalenzrelation.
K¨onnen Partitionen als ¨Aquivalenzklassen betrachten und umgekehrt.
Aquivalenzklassen ¨
Definition (¨Aquivalenzklassen)
SeiR eine ¨Aquivalenzrelation auf der Menge M. DieAquivalenzklasse¨ von a∈M ist die Menge
[a] ={b∈M |a∼b}.
Die Menge aller ¨Aquivalenzklassen ist eine Partition vonM. Umgekehrt:
F¨ur PartitionP definiere R={(x,y)| ∃B∈P :x,y∈B}
(alsox ∼y genau dann, wennx und y im gleichen Block).
Dann ist R eine ¨Aquivalenzrelation.
K¨onnen Partitionen als ¨Aquivalenzklassen betrachten und umgekehrt.
Aquivalenzklassen ¨
Definition (¨Aquivalenzklassen)
SeiR eine ¨Aquivalenzrelation auf der Menge M. DieAquivalenzklasse¨ von a∈M ist die Menge
[a] ={b∈M |a∼b}.
Die Menge aller ¨Aquivalenzklassen ist eine Partition vonM. Umgekehrt:
F¨ur PartitionP definiere R={(x,y)| ∃B∈P :x,y∈B}
(alsox ∼y genau dann, wennx und y im gleichen Block).
Dann ist R eine ¨Aquivalenzrelation.
K¨onnen Partitionen als ¨Aquivalenzklassen betrachten und umgekehrt.
Union-Find Zusammenhangskomponenten und ¨Aquivalenzklassen
Union-Find und ¨ Aquivalenzen
Gegeben: endliche MengeM,
Sequenz s von ¨Aquivalenzen a∼b ¨uberM Fasse ¨Aquivalenzen als Kanten in Graphen mit Knotenmenge M auf.
Die Zusammenhangskomponenten entsprechen den Aquivalenzklassen der feinsten ¨¨ Aquivalenzrelation, die alle ¨Aquivalenzen auss enth¨alt.
keine
”unn¨otigen“ ¨Aquivalenzen
Bestimmung der ¨Aquivalenzklassen verwenden.
Union-Find Zusammenhangskomponenten und ¨Aquivalenzklassen
Union-Find und ¨ Aquivalenzen
Gegeben: endliche MengeM,
Sequenz s von ¨Aquivalenzen a∼b ¨uberM Fasse ¨Aquivalenzen als Kanten in Graphen mit Knotenmenge M auf.
Die Zusammenhangskomponenten entsprechen den Aquivalenzklassen der feinsten ¨¨ Aquivalenzrelation, die alle ¨Aquivalenzen auss enth¨alt.
keine
”unn¨otigen“ ¨Aquivalenzen
Bestimmung der ¨Aquivalenzklassen verwenden.
Union-Find Zusammenhangskomponenten und ¨Aquivalenzklassen
Union-Find und ¨ Aquivalenzen
Gegeben: endliche MengeM,
Sequenz s von ¨Aquivalenzen a∼b ¨uberM Fasse ¨Aquivalenzen als Kanten in Graphen mit Knotenmenge M auf.
Die Zusammenhangskomponenten entsprechen den Aquivalenzklassen der¨ feinsten ¨Aquivalenzrelation, die alle ¨Aquivalenzen auss enth¨alt.
keine
”unn¨otigen“ ¨Aquivalenzen
Bestimmung der ¨Aquivalenzklassen verwenden.
Union-Find und ¨ Aquivalenzen
Gegeben: endliche MengeM,
Sequenz s von ¨Aquivalenzen a∼b ¨uberM Fasse ¨Aquivalenzen als Kanten in Graphen mit Knotenmenge M auf.
Die Zusammenhangskomponenten entsprechen den Aquivalenzklassen der¨ feinsten ¨Aquivalenzrelation, die alle ¨Aquivalenzen auss enth¨alt.
keine
”unn¨otigen“ ¨Aquivalenzen Wir k¨onnen dieUnion-Find-Datenstruktur zur Bestimmung der ¨Aquivalenzklassenverwenden.