<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://wiki-de.moshellshocker.dns64.de/index.php?action=history&amp;feed=atom&amp;title=Union-Find-Struktur</id>
	<title>Union-Find-Struktur - Versionsgeschichte</title>
	<link rel="self" type="application/atom+xml" href="https://wiki-de.moshellshocker.dns64.de/index.php?action=history&amp;feed=atom&amp;title=Union-Find-Struktur"/>
	<link rel="alternate" type="text/html" href="https://wiki-de.moshellshocker.dns64.de/index.php?title=Union-Find-Struktur&amp;action=history"/>
	<updated>2026-06-08T12:29:11Z</updated>
	<subtitle>Versionsgeschichte dieser Seite in Wikipedia (Deutsch) – Lokale Kopie</subtitle>
	<generator>MediaWiki 1.43.8</generator>
	<entry>
		<id>https://wiki-de.moshellshocker.dns64.de/index.php?title=Union-Find-Struktur&amp;diff=414475&amp;oldid=prev</id>
		<title>imported&gt;Hpod: log -&gt; log_2</title>
		<link rel="alternate" type="text/html" href="https://wiki-de.moshellshocker.dns64.de/index.php?title=Union-Find-Struktur&amp;diff=414475&amp;oldid=prev"/>
		<updated>2024-08-29T09:53:59Z</updated>

		<summary type="html">&lt;p&gt;log -&amp;gt; log_2&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Neue Seite&lt;/b&gt;&lt;/p&gt;&lt;div&gt;Eine &amp;#039;&amp;#039;&amp;#039;Union-Find-Datenstruktur&amp;#039;&amp;#039;&amp;#039; verwaltet die [[Partition (Mengenlehre)|Partition]] einer [[Menge (Mathematik)|Menge]]. Der [[Abstrakter Datentyp|abstrakte Datentyp]] wird durch die Menge der drei Operationen &amp;#039;&amp;#039;{Union, Find, Make-Set}&amp;#039;&amp;#039; gebildet. Union-Find-Strukturen dienen zur Verwaltung von Zerlegungen in disjunkte Mengen. Dabei bekommt jede Menge der Zerlegung ein kanonisches Element zugeordnet, welches als Name der Menge dient. &amp;#039;&amp;#039;Union&amp;#039;&amp;#039; vereinigt zwei solche Mengen, &amp;#039;&amp;#039;Find(x)&amp;#039;&amp;#039; bestimmt das kanonische Element derjenigen Menge, die &amp;#039;&amp;#039;x&amp;#039;&amp;#039; enthält, und &amp;#039;&amp;#039;Make-Set&amp;#039;&amp;#039; erzeugt eine Elementarmenge &amp;#039;&amp;#039;{x}&amp;#039;&amp;#039; mit dem kanonischen Element &amp;#039;&amp;#039;x&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== Definition ==&lt;br /&gt;
Eine endliche Menge &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; sei in die [[disjunkt]]en [[Klasse (Mengenlehre)|Klassen]] &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; partitioniert:&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;math&amp;gt;S = X_0 \uplus X_1 \uplus X_2 \uplus \ldots \uplus X_k&amp;lt;/math&amp;gt; mit &amp;lt;math&amp;gt;X_i \cap X_j = \varnothing \quad\forall i, j \in \lbrace 0, 1, \ldots, k \rbrace, i \neq j&amp;lt;/math&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Zu jeder Klasse &amp;lt;math&amp;gt;X_i&amp;lt;/math&amp;gt; wird ein Repräsentant &amp;lt;math&amp;gt;r_i \in X_i&amp;lt;/math&amp;gt; ausgewählt.&lt;br /&gt;
Die zugehörige Union-Find-Struktur unterstützt die folgenden Operationen effizient:&lt;br /&gt;
* Init(&amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt;): Initialisiert die Struktur und bildet für jedes &amp;lt;math&amp;gt;x \in S&amp;lt;/math&amp;gt; eine eigene Klasse mit &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; als Repräsentant.&lt;br /&gt;
* Union(&amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt;): Vereinigt die beiden Klassen, die zu den beiden Repräsentanten &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;s&amp;lt;/math&amp;gt; gehören, und bestimmt &amp;lt;math&amp;gt;r&amp;lt;/math&amp;gt; zum neuen Repräsentanten der neuen Klasse.&lt;br /&gt;
* Find(&amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;): Bestimmt zu &amp;lt;math&amp;gt;x \in S&amp;lt;/math&amp;gt; den eindeutigen Repräsentanten, zu dessen Klasse &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; gehört.&lt;br /&gt;
&lt;br /&gt;
== Implementierung ==&lt;br /&gt;
Eine triviale Implementierung speichert die Zugehörigkeiten zwischen den Elementen aus &amp;lt;math&amp;gt;S&amp;lt;/math&amp;gt; und den Repräsentanten &amp;lt;math&amp;gt;r_i&amp;lt;/math&amp;gt; in einem [[Feld (Datentyp)|Array]]. Für kürzere Laufzeiten werden jedoch in der Praxis Mengen von [[Baum (Graphentheorie)|Bäumen]] verwendet. Dabei werden die Repräsentanten in den [[Wurzel (Graphentheorie)|Wurzeln]] der Bäume gespeichert, die anderen Elemente der jeweiligen Klasse in den [[Knoten (Graphentheorie)|Knoten]] darunter.&lt;br /&gt;
&lt;br /&gt;
* Union(&amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;, &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt;): Hängt die Wurzel des niedrigeren Baumes als neues Kind unter die Wurzel des höheren Baumes (gewichtete Vereinigung). Falls nun &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; Kind von &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; ist, werden &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; und &amp;lt;math&amp;gt;y&amp;lt;/math&amp;gt; vertauscht.&lt;br /&gt;
* Find(&amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt;): Wandert vom Knoten &amp;lt;math&amp;gt;x&amp;lt;/math&amp;gt; aus den Pfad innerhalb des Baumes nach oben bis zur Wurzel und gibt diese als Ergebnis zurück.&lt;br /&gt;
&lt;br /&gt;
== Heuristiken ==&lt;br /&gt;
Um die Operationen &amp;#039;&amp;#039;Find&amp;#039;&amp;#039; und &amp;#039;&amp;#039;Union&amp;#039;&amp;#039; zu beschleunigen, gibt es die [[Heuristik]]en &amp;#039;&amp;#039;union by size, union by rank&amp;#039;&amp;#039; (Rang- und Größenheuristik) und &amp;#039;&amp;#039;Pfadkompression&amp;#039;&amp;#039;, wobei sich &amp;#039;&amp;#039;union by size&amp;#039;&amp;#039; und &amp;#039;&amp;#039;union by rank&amp;#039;&amp;#039; gegenseitig ausschließen und &amp;#039;&amp;#039;union by rank&amp;#039;&amp;#039; oft in Kombination mit &amp;#039;&amp;#039;Pfadkompression&amp;#039;&amp;#039; verwendet wird.&lt;br /&gt;
&lt;br /&gt;
=== {{Anker|size}} Union by size ===&lt;br /&gt;
Bei der [[Operation (Informatik)|Operation]] &amp;#039;&amp;#039;Union(r,s)&amp;#039;&amp;#039; wird der [[Baum (Graphentheorie)|Baum]], der in Summe weniger Knoten hat, sprich kleiner ist, unter den größeren Baum gehängt. Damit verhindert man, dass einzelne Teilbäume zu Listen entarten können wie bei der naiven Implementation (r wird in jedem Fall Wurzel des neuen Teilbaums). Die Tiefe eines Teilbaums T kann damit nicht größer als &amp;lt;math&amp;gt;\log_2 \left|T\right|&amp;lt;/math&amp;gt; werden. Mit dieser Heuristik verringert sich die Worst-Case-Laufzeit von &amp;#039;&amp;#039;Find&amp;#039;&amp;#039; von &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt; auf &amp;lt;math&amp;gt;O(\log n)&amp;lt;/math&amp;gt;. Für eine effiziente Implementation führt man bei jeder Wurzel die Anzahl der Elemente im Teilbaum mit.&lt;br /&gt;
&lt;br /&gt;
=== {{Anker|rank}} Union by rank ===&lt;br /&gt;
Wird ausschließlich die Heuristik &amp;#039;&amp;#039;union by rank&amp;#039;&amp;#039; verwendet, entspricht der Rang (&amp;#039;&amp;#039;rank&amp;#039;&amp;#039;) einer Wurzel der Höhe ihres Unter[[Baum (Graphentheorie)|baumes]].&lt;br /&gt;
Es wird stets der Baum mit dem niedrigeren Rang dem mit dem höheren angehängt. Bei gleichem Rang kann die Verkettung der Bäume beliebig gewählt werden. Somit kann der Rang eines Knoten nur wachsen, wenn zwei Bäume des gleichen Rangs aneinander gehängt werden.&lt;br /&gt;
&lt;br /&gt;
Daraus folgt insbesondere:&lt;br /&gt;
* Knoten ohne Kinder haben den Rang 0&lt;br /&gt;
* Wenn die zu kombinierenden [[Baum (Graphentheorie)|Bäume]] den gleichen Rang haben, ist der Rang der resultierenden Baumes um 1 größer&lt;br /&gt;
&lt;br /&gt;
Man kann zur weiteren Effizienzsteigerung &amp;#039;&amp;#039;union by rank&amp;#039;&amp;#039; in Kombination mit &amp;#039;&amp;#039;Pfadkompression&amp;#039;&amp;#039; verwenden. In diesem Fall wird deutlich, weshalb die Bezeichnung des Rangs anstelle der der Tiefe verwendet wird: Die &amp;#039;&amp;#039;Pfadkompression&amp;#039;&amp;#039; verringert die Höhe von Bäumen, während ihr Rang bestehen bleibt. Siehe auch &amp;#039;&amp;#039;Pfadkompression&amp;#039;&amp;#039;.&amp;lt;ref&amp;gt;[https://algorithms.tutorialhorizon.com/disjoint-set-union-find-algorithm-union-by-rank-and-path-compression/ Disjoint Set | Union-Find Algorithm – Union by rank and path compression]. In: algorithms.tutorialhorizon.com (englisch)&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;{{Webarchiv|url=http://www.mathblog.dk/disjoint-set-data-structure/ |wayback=20161202120715 |text=Disjoint-set data structure |archiv-bot=2023-02-02 09:07:22 InternetArchiveBot }}. In: mathblog.dk&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Pseudocode ====&lt;br /&gt;
 &amp;#039;&amp;#039;&amp;#039;function&amp;#039;&amp;#039;&amp;#039; &amp;#039;&amp;#039;Union&amp;#039;&amp;#039;(&amp;#039;&amp;#039;x&amp;#039;&amp;#039;, &amp;#039;&amp;#039;y&amp;#039;&amp;#039;)&lt;br /&gt;
 {&lt;br /&gt;
     &amp;#039;&amp;#039;// Knoten durch die Wurzeln des Baums ersetzen&amp;#039;&amp;#039;&lt;br /&gt;
     &amp;#039;&amp;#039;x&amp;#039;&amp;#039; = &amp;#039;&amp;#039;Find&amp;#039;&amp;#039;(&amp;#039;&amp;#039;x&amp;#039;&amp;#039;);&lt;br /&gt;
     &amp;#039;&amp;#039;y&amp;#039;&amp;#039; = &amp;#039;&amp;#039;Find&amp;#039;&amp;#039;(&amp;#039;&amp;#039;y&amp;#039;&amp;#039;);&lt;br /&gt;
     &amp;#039;&amp;#039;&amp;#039;if&amp;#039;&amp;#039;&amp;#039; (&amp;#039;&amp;#039;x&amp;#039;&amp;#039; = &amp;#039;&amp;#039;y&amp;#039;&amp;#039;) &amp;#039;&amp;#039;// Wenn x und y schon zur selben Menge gehören, wird die Funktion verlassen&amp;#039;&amp;#039;&lt;br /&gt;
     {&lt;br /&gt;
         &amp;#039;&amp;#039;&amp;#039;return;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
     }&lt;br /&gt;
     &amp;#039;&amp;#039;&amp;#039;if&amp;#039;&amp;#039;&amp;#039; (&amp;#039;&amp;#039;x&amp;#039;&amp;#039;.rank &amp;lt; &amp;#039;&amp;#039;y&amp;#039;&amp;#039;.rank) &amp;#039;&amp;#039;// Wenn der Rang von x kleiner als der Rang von y ist, wird y zur neuen Wurzel&amp;#039;&amp;#039;&lt;br /&gt;
     {&lt;br /&gt;
         &amp;#039;&amp;#039;x&amp;#039;&amp;#039;.parent = &amp;#039;&amp;#039;y&amp;#039;&amp;#039;;&lt;br /&gt;
     }&lt;br /&gt;
     &amp;#039;&amp;#039;&amp;#039;else if&amp;#039;&amp;#039;&amp;#039; (&amp;#039;&amp;#039;x&amp;#039;&amp;#039;.rank &amp;gt; &amp;#039;&amp;#039;y&amp;#039;&amp;#039;.rank) &amp;#039;&amp;#039;// Wenn der Rang von x größer als der Rang von y ist, wird x zur neuen Wurzel&amp;#039;&amp;#039;&lt;br /&gt;
     {&lt;br /&gt;
         &amp;#039;&amp;#039;y&amp;#039;&amp;#039;.parent = &amp;#039;&amp;#039;x&amp;#039;&amp;#039;;&lt;br /&gt;
     }&lt;br /&gt;
     &amp;#039;&amp;#039;&amp;#039;else&amp;#039;&amp;#039;&amp;#039; &amp;#039;&amp;#039;// Wenn die Ränge gleich sind, wird y zur neuen Wurzel und der Rang von y inkrementiert&amp;#039;&amp;#039;&lt;br /&gt;
     {&lt;br /&gt;
         &amp;#039;&amp;#039;x&amp;#039;&amp;#039;.parent = &amp;#039;&amp;#039;y&amp;#039;&amp;#039;;&lt;br /&gt;
         &amp;#039;&amp;#039;y&amp;#039;&amp;#039;.rank = &amp;#039;&amp;#039;y&amp;#039;&amp;#039;.rank + 1;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Pfadkompression ===&lt;br /&gt;
Um spätere &amp;#039;&amp;#039;Find(x)&amp;#039;&amp;#039;-Suchvorgänge zu beschleunigen, versucht man die Wege vom besuchten Knoten zur zugehörigen Wurzel zu verkürzen.&lt;br /&gt;
&lt;br /&gt;
==== Maximale Verkürzung (Wegkompression) ====&lt;br /&gt;
Nach dem Ausführen von &amp;#039;&amp;#039;Find(x)&amp;#039;&amp;#039; werden alle Knoten auf dem Pfad von &amp;#039;&amp;#039;x&amp;#039;&amp;#039; zur Wurzel direkt unter die Wurzel gesetzt. Man beachte hierbei, dass die Operation &amp;#039;&amp;#039;Union(x,y)&amp;#039;&amp;#039; die Operation &amp;#039;&amp;#039;Find(x)&amp;#039;&amp;#039; verwendet. Dieses Verfahren bringt im Vergleich zu den beiden folgenden den größten Kostenvorteil für nachfolgende Aufrufe von Find für einen Knoten im gleichen Teilbaum. Zwar muss dabei jeder Knoten auf dem Pfad zwischen Wurzel und &amp;#039;&amp;#039;x&amp;#039;&amp;#039; zweimal betrachtet werden, für die [[Zeitkomplexität|asymptotische Laufzeit]] ist das jedoch egal.&lt;br /&gt;
&lt;br /&gt;
==== Pseudocode ====&lt;br /&gt;
 &amp;#039;&amp;#039;&amp;#039;function&amp;#039;&amp;#039;&amp;#039; &amp;#039;&amp;#039;Find&amp;#039;&amp;#039;(&amp;#039;&amp;#039;x&amp;#039;&amp;#039;)&lt;br /&gt;
 {&lt;br /&gt;
     &amp;#039;&amp;#039;&amp;#039;if&amp;#039;&amp;#039;&amp;#039; (&amp;#039;&amp;#039;x&amp;#039;&amp;#039;.parent ≠ &amp;#039;&amp;#039;x&amp;#039;&amp;#039;)&lt;br /&gt;
     {&lt;br /&gt;
         &amp;#039;&amp;#039;x&amp;#039;&amp;#039;.parent = &amp;#039;&amp;#039;Find&amp;#039;&amp;#039;(&amp;#039;&amp;#039;x&amp;#039;&amp;#039;.parent);&lt;br /&gt;
         &amp;#039;&amp;#039;&amp;#039;return&amp;#039;&amp;#039;&amp;#039; &amp;#039;&amp;#039;x&amp;#039;&amp;#039;.parent;&lt;br /&gt;
     }&lt;br /&gt;
     &amp;#039;&amp;#039;&amp;#039;else&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
     {&lt;br /&gt;
         &amp;#039;&amp;#039;&amp;#039;return&amp;#039;&amp;#039;&amp;#039; &amp;#039;&amp;#039;x&amp;#039;&amp;#039;;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Aufteilungsmethode (splitting) ==&lt;br /&gt;
Während des Durchlaufes lässt man jeden Knoten auf seinen bisherigen Großvater zeigen, falls vorhanden. Damit wird ein durchlaufender Pfad in zwei der halben Länge zerlegt.&lt;br /&gt;
&lt;br /&gt;
=== Halbierungsmethode (halving) ===&lt;br /&gt;
Während des Durchlaufes lässt man jeden zweiten Knoten auf seinen bisherigen Großvater zeigen.&lt;br /&gt;
&lt;br /&gt;
Diese Methoden haben beide dieselben amortisierten Kosten wie die oberste Kompressionsmethode (Knoten unter die Wurzel schreiben). Alle Kompressionsmethoden beschleunigen zukünftige &amp;#039;&amp;#039;Find(x)&amp;#039;&amp;#039;-Operationen.&lt;br /&gt;
&lt;br /&gt;
== Laufzeiten ==&lt;br /&gt;
Union-Find-Datenstrukturen ermöglichen die Ausführung der obigen Operationen mit den folgenden [[Laufzeit (Informatik)|Laufzeiten]] (&amp;lt;math&amp;gt;n=\left| S \right|&amp;lt;/math&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
Triviale Implementierung mit einem Array A (A[i] = x: Knoten i liegt in der Menge x), worst-case: Find &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;, Union: &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Implementierung mit Bäumen:&lt;br /&gt;
* ohne Heuristiken: Find &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;, Union &amp;lt;math&amp;gt;O(n)&amp;lt;/math&amp;gt;&lt;br /&gt;
* mit Union-By-Size, worst-case: Find &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt;, Union &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
* mit Union-By-Size, Pfadkompression, worst-case: Find &amp;lt;math&amp;gt;O(\log(n))&amp;lt;/math&amp;gt;, Union &amp;lt;math&amp;gt;O(1)&amp;lt;/math&amp;gt;&lt;br /&gt;
* mit Union-By-Size, Pfadkompression, Folge von &amp;#039;&amp;#039;m&amp;#039;&amp;#039; Find- und &amp;#039;&amp;#039;n-1&amp;#039;&amp;#039; Union-Operationen: &amp;lt;math&amp;gt;O\left(n + m \cdot\alpha(n)\right)&amp;lt;/math&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dabei ist &amp;lt;math&amp;gt;\alpha(n)&amp;lt;/math&amp;gt; die [[Umkehrfunktion]] der [[Ackermannfunktion]] &amp;lt;math&amp;gt;A(n,n)&amp;lt;/math&amp;gt;. Sie wächst sehr langsam und ist für alle „praktischen“ Eingaben (&amp;lt;math&amp;gt;n&amp;lt;10^{80}&amp;lt;/math&amp;gt;) kleiner als 5. Im Jahr 1977 hat [[Robert Tarjan]] gezeigt, dass für eine Sequenz &amp;#039;&amp;#039;m&amp;#039;&amp;#039; Find- und &amp;#039;&amp;#039;n-1&amp;#039;&amp;#039; Union-Operationen jede Datenstruktur im Pointer-Maschinen Modell eine Laufzeit von &amp;lt;math&amp;gt;\Omega\left(n + m \cdot\alpha(n)\right)&amp;lt;/math&amp;gt; benötigt.&amp;lt;ref&amp;gt;{{cite journal |first=Robert E. |last =Tarjan |title = A class of algorithms which require nonlinear time to maintain disjoint sets |language=en |journal = Journal of Computer and System Sciences |volume = 18 |issue = 2 |pages = 110–127 |year = 1979 |doi = 10.1016/0022-0000(79)90042-4}}&amp;lt;/ref&amp;gt;&lt;br /&gt;
Fredman und Saks haben 1989 gezeigt, dass diese untere Schranke auch im allgemeineren Cell-Probe Modell gilt.&amp;lt;ref&amp;gt;{{Cite conference |first=M. |last=Fredman |first2=M. |last2=Saks |title=The cell probe complexity of dynamic data structures |conference=Proceedings of the Twenty-First Annual ACM Symposium on Theory of Computing |pages=345–354 |date=May 1989 |language=en |doi=10.1145/73007.73040}}&amp;lt;/ref&amp;gt; Daraus folgt, dass die Datenstruktur mit Union-By-Size und Pfadkompression eine asymptotisch optimale Laufzeit besitzt, und es keine Union-Find-Datenstruktur geben kann, die sowohl &amp;#039;&amp;#039;Find&amp;#039;&amp;#039; als auch &amp;#039;&amp;#039;Union&amp;#039;&amp;#039; [[Amortisierte Analyse|amortisiert]] in O(1) ausführt.&lt;br /&gt;
&lt;br /&gt;
== Anwendungen ==&lt;br /&gt;
Union-Find-Strukturen eignen sich gut, um [[Zusammenhang von Graphen|Zusammenhangskomponenten von Graphen]] zu verwalten.&lt;br /&gt;
&lt;br /&gt;
Union-Find-Strukturen können auch genutzt werden, um effizient herauszufinden, ob ein Graph [[Zyklus (Graphentheorie)|Zyklen]] enthält.&lt;br /&gt;
&lt;br /&gt;
Das folgende [[Computerprogramm]] in der [[Programmiersprache]] [[C++]] zeigt die Implementierung eines [[Ungerichteter Graph|ungerichteten Graphen]] mit einem Array von [[Kante (Graphentheorie)|Kanten]]. Der ungerichtete Graph wird als [[Datentyp]] &amp;#039;&amp;#039;UndirectedGraph&amp;#039;&amp;#039; deklariert. Bei der Ausführung des Programms wird die [[Methode (Programmierung)|Methode]] &amp;#039;&amp;#039;main&amp;#039;&amp;#039; verwendet, das auf der Konsole ausgibt, ob der gegebene Graph Zyklen enthält. Die [[Funktion (Programmierung)|Funktion]] &amp;#039;&amp;#039;unionSubsets&amp;#039;&amp;#039; verwendet &amp;#039;&amp;#039;union by rank&amp;#039;&amp;#039;, um zwei [[Teilmenge]]n von Kanten des Graphen zu vereinigen.&amp;lt;ref&amp;gt;[https://www.geeksforgeeks.org/union-find-algorithm-set-2-union-by-rank/ Union-Find Algorithm | Set 2 (Union By Rank and Path Compression)]. In: GeeksforGeeks (englisch)&amp;lt;/ref&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c++&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;iostream&amp;gt;&lt;br /&gt;
using namespace std;&lt;br /&gt;
&lt;br /&gt;
// Deklariert den Datentyp für die Kanten des Graphen&lt;br /&gt;
struct Edge&lt;br /&gt;
{&lt;br /&gt;
    int startIndex, endIndex;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Deklariert den Datentyp für den ungerichteten Graphen&lt;br /&gt;
struct UndirectedGraph&lt;br /&gt;
{&lt;br /&gt;
    int numberOfVertices, numberOfEdges;&lt;br /&gt;
    Edge* edges; // Pointer auf das Array für die Kanten&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Deklariert den Datentyp für Teilmengen der Kantenmenge des ungerichteten Graphen&lt;br /&gt;
struct subset&lt;br /&gt;
{&lt;br /&gt;
    int parent;&lt;br /&gt;
    int rank;&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
// Diese Funktion erzeugt einen ungerichteten Graphen&lt;br /&gt;
struct UndirectedGraph* createGraph(int numberOfVertices, int numberOfEdges)&lt;br /&gt;
{&lt;br /&gt;
    struct UndirectedGraph* undirectedGraph = new UndirectedGraph();&lt;br /&gt;
    undirectedGraph-&amp;gt;numberOfVertices = numberOfVertices;&lt;br /&gt;
    undirectedGraph-&amp;gt;numberOfEdges = numberOfEdges;&lt;br /&gt;
    undirectedGraph-&amp;gt;edges = new Edge[numberOfEdges];&lt;br /&gt;
    return undirectedGraph;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Diese rekursive Funktion gibt den Index der Wurzel der Teilmenge mit dem Index i zurück&lt;br /&gt;
int find(subset subsets[], int i)&lt;br /&gt;
{&lt;br /&gt;
    // Setzt Index der Wurzel auf den Index der Wurzel der Teilmenge mit dem Index i&lt;br /&gt;
    if (subsets[i].parent != i)&lt;br /&gt;
    {&lt;br /&gt;
        subsets[i].parent = find(subsets, subsets[i].parent); // Rekursiver Aufruf der Funktion&lt;br /&gt;
    }&lt;br /&gt;
    return subsets[i].parent;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Diese Methode bildet die Vereinigungsmenge der zwei Teilmengen mit den Indexen index1 und index2&lt;br /&gt;
void unionSubsets(subset subsets[], int index1, int index2)&lt;br /&gt;
{&lt;br /&gt;
    int newIndex1 = find(subsets, index1); // Index der Teilmenge mit dem Index index1&lt;br /&gt;
    int newIndex2 = find(subsets, index2); // Index der Teilmenge mit dem Index index2&lt;br /&gt;
     // Hängt den Teilbaum mit dem niedrigeren Rang unter die Wurzel des Baums mit dem höheren Rang&lt;br /&gt;
    if (subsets[newIndex1].rank &amp;lt; subsets[newIndex2].rank)&lt;br /&gt;
    {&lt;br /&gt;
        subsets[newIndex1].parent = newIndex2;&lt;br /&gt;
    }&lt;br /&gt;
    else if (subsets[newIndex1].rank &amp;gt; subsets[newIndex2].rank)&lt;br /&gt;
    {&lt;br /&gt;
        subsets[newIndex2].parent = newIndex1;&lt;br /&gt;
    }&lt;br /&gt;
    else // Wenn die Teilbäume denselben Rang haben, wird der Rang des einen Baums erhöht und der andere Baum unter die Wurzel des anderen Baums gehängt&lt;br /&gt;
    {&lt;br /&gt;
        subsets[newIndex2].parent = newIndex1;&lt;br /&gt;
        subsets[newIndex1].rank++;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Diese Methode prüft, ob der Graph Zyklen enthält&lt;br /&gt;
bool containsCycle(struct UndirectedGraph* graph)&lt;br /&gt;
{&lt;br /&gt;
    int numberOfVertices = graph-&amp;gt;numberOfVertices;&lt;br /&gt;
    int numberOfEdges = graph-&amp;gt;numberOfEdges;&lt;br /&gt;
    Edge* edges = graph-&amp;gt;edges; // Pointer auf das Array für die Kanten&lt;br /&gt;
    subset* subsets = new subset[numberOfVertices]; // Deklariert ein Array für die Teilmengen der Kantenmenge&lt;br /&gt;
    for (int i = 0; i &amp;lt; numberOfVertices; i++) // for-Schleife, die Teilmengen mit einzelnen Kanten erzeugt&lt;br /&gt;
    {&lt;br /&gt;
        subsets[i].parent = i;&lt;br /&gt;
        subsets[i].rank = 0;&lt;br /&gt;
    }&lt;br /&gt;
    for (int i = 0; i &amp;lt; numberOfEdges; i++) // foreach-Schleife, die alle Kanten durchläuft&lt;br /&gt;
    {&lt;br /&gt;
        Edge nextEdge = edges[i++]; // Weist die verbleibende Kante mit dem kleinsten Kantengewicht zu und erhöht den Kantenindex für die nächste Iteration um 1&lt;br /&gt;
        int index1 = find(subsets, nextEdge.startIndex); // Index der Wurzel der Teilmenge mit dem Index nextEdge.startIndex&lt;br /&gt;
        int index2 = find(subsets, nextEdge.endIndex); // Index der Wurzel der Teilmenge mit dem Index nextEdge.endIndex&lt;br /&gt;
        if (index1 == index2) // Wenn die Indexe der Wurzeln, also auch die Mengen übereinstimmen, enthält der Graph einen Zyklus&lt;br /&gt;
        {&lt;br /&gt;
            return true;&lt;br /&gt;
        }&lt;br /&gt;
        unionSubsets(subsets, index1, index2);&lt;br /&gt;
    }&lt;br /&gt;
    return false;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Hauptfunktion die das Programm ausführt&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    int numberOfVertices = 3;&lt;br /&gt;
    int numberOfEdges = 3;&lt;br /&gt;
&lt;br /&gt;
    // Erzeugt den ungerichteten Graphen mit den gegebenen Kanten&lt;br /&gt;
    struct UndirectedGraph* undirectedGraph = createGraph(numberOfVertices, numberOfEdges);&lt;br /&gt;
    // Initialisiert das Array mit 3 Kanten&lt;br /&gt;
    Edge* edges = undirectedGraph-&amp;gt;edges;&lt;br /&gt;
    edges[0].startIndex = 0;&lt;br /&gt;
    edges[0].endIndex = 1;&lt;br /&gt;
    edges[1].startIndex = 1;&lt;br /&gt;
    edges[1].endIndex = 2;&lt;br /&gt;
    edges[2].startIndex = 0;&lt;br /&gt;
    edges[2].endIndex = 2;&lt;br /&gt;
    if (containsCycle(undirectedGraph)) // Aufruf der Methode&lt;br /&gt;
    {&lt;br /&gt;
        cout &amp;lt;&amp;lt; &amp;quot;Der Graph enthält Zyklen.&amp;quot; &amp;lt;&amp;lt; endl; // Ausgabe auf der Konsole&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
        cout &amp;lt;&amp;lt; &amp;quot;Der Graph enthält keine Zyklen.&amp;quot; &amp;lt;&amp;lt; endl; // Ausgabe auf der Konsole&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;Der [[Algorithmus von Kruskal]] erfordert beispielsweise eine solche Datenstruktur, um effizient implementiert werden zu können.&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [https://www.cs.usfca.edu/~galles/visualization/DisjointSets.html Demonstration von Union-Find]&lt;br /&gt;
&lt;br /&gt;
== Einzelnachweise ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Datenstruktur]]&lt;br /&gt;
[[Kategorie:Theoretische Informatik]]&lt;/div&gt;</summary>
		<author><name>imported&gt;Hpod</name></author>
	</entry>
</feed>