diff --git a/300_Datenstrukturen.tex b/300_Datenstrukturen.tex
index dccd330f2147c3c247eeb1ed771b509f5bd94521..6c77917c0aaa7864f4185b91b1d5ad5e5f6e1bb4 100644
--- a/300_Datenstrukturen.tex
+++ b/300_Datenstrukturen.tex
@@ -4,4 +4,4 @@
 \include{302_Hashtabellen}
 \include{303_Binaerbaeume}
 \include{304_Halden}
-%\include{305_2-4-Baeume}
+\include{305_RBT}
diff --git a/305_RBT.tex b/305_RBT.tex
new file mode 100644
index 0000000000000000000000000000000000000000..5e5c5bdc779032d5841fc95400498c49ce7202bc
--- /dev/null
+++ b/305_RBT.tex
@@ -0,0 +1,232 @@
+\section{Red-Black-Trees (RBT)}
+
+\subsection{Balancierte Bäume und Definition}
+
+Wir haben in vorherigen Beispielen und Übungsaufgaben gesehen, dass Binärbäume leicht degenerieren können und ihre Höhe
+dann nicht mehr $h \simeq \log_2(n)$ entspricht. Da alle Baumoperationen in $\mathcal{O}(h)$ liegen, ist das ein fatales
+Problem. Der Lösungsansatz liegt auf der Hand: Wir müssen die Bäume balanciert halten.
+
+Dazu gibt es mehrere Ansätze, der älteste ist der AVL-Baum (1962). In dieser Verfeinerung eine Binärbaums wird sofort ein relativ ressourcensparender
+Balanciermechanismus, das Rotieren (todo: PIC) gestartet, sobald die Höhe des Linken Unterbaums sich von der Höhe des rechten Unterbaums um
+$\leq 2$ unterscheidet. Dieses Modell ist sehr strikt und einfach zu verstehen, aber durch die Striktheit wird der
+Balanciermechanismus öfter aktiviert, als eigentlich nötig wäre.
+
+Eine andere Möglichkeit sind $(2-4)$-Bäume. Diese sind keine Binärbäume, sondern jedes nicht-Blatt hat zwei, drei oder
+vier Kinder.
+Durch das Speichern der Nutzdaten in Blättern ist damit die Höhe $\log_4 n ≤ h ≤ log_2 n$ garantiert.
+Wird beim Einfügen die Obergrenze von vier Kindern verletzt, wird der entsprechende Knoten in zwei Knoten
+aufgeteilt. Damit kann diese Verletzung nach oben weitergegeben werden, aber nach spätestens $h$ Schritten ist diese
+Bedingung wieder erfüllt. 
+Analog kann beim Löschen eines Blattes die Untergrenze verletzt werden. Zur Korrektur werden nun Nachbarknoten
+zusammengelegt, potentiell wieder rekursiv bis zur Wurzel.
+
+Da nicht-Binärbäume aufwendig in der Formalisierung und Implementierung sind, schauen wir uns ein sehr verwandtes Konzept an:
+Den Red-Black-Tree.
+
+\begin{definition}[RBT]
+  Red-Black-Trees (RBT) über einen Datentyp $D$ sind Binäre Suchbäume, bei denen jeder Knoten zusätzlich eine Farbe aus
+  $\{R,B\}$ abspeichert.
+
+  Folgende zwei Invarianzen müssen zudem immer gewahrt werden:
+  \begin{itemize}
+    \item[lokal] Ein Roter Knoten darf keinen roten Kindknoten haben.
+    \item[global] Jeder Pfad von der Wurzel zu einem Blatt durchschreitet die gleiche Anzahl schwarzer Knoten.
+  \end{itemize}
+\end{definition}
+
+Per Konvention nehmen wir an, dass der Wurzelknoten immer Schwarz ist.
+Wenn der kürzeste vorstellbare Pfad von der Wurzel zu einem Blatt in einem RBT-Tree durch $l$ ausschließlich durch schwarze Knoten geht, ist der längste
+Pfad dann durch $2l$ Knoten, immer einen Schwarzen und einen roten. Dadurch ist gewährleistet, dass das höchste Blatt
+maximal halb so hoch hängt wie eins auf der untersten Ebene.
+
+Dadurch folgt:
+\begin{lemma}
+  In einem nichtlehren Red-Black-Tree der Größe $n$ liegt die Höhe $h$ in $Θ(\log n)$.
+  \label{lemma:RBT_height}
+\end{lemma}
+Die Details des Beweises sind eine Übungsaufgabe.
+
+\subsection{Einfügen in Red-Black-Trees}
+
+Da RBT insbesondere Binäre Suchbäume sind, sind die Operationen zum Suchen von Elementen, dem Finden des Maximums,
+Minimums, Nachfolgers etc identisch.
+
+Auch die grundsätzliche Idee des Einfügens bleibt gleich: Wie bei BST gehen wir von der Wurzel abwärts und suchen dabei
+den passenden Platz für das neue Element. Eingefügte Elemente sind also erst einmal Blätter.
+
+Würde man das neue Blatt schwarz färben, wäre sofort die globale Invarianz verletzt. Daher färben wir das neue Blatt
+rot. Allerdings kann es dabei zu einer Verletzung der lokalen Invarianz kommen. Skizze \ref{fig:rbt_balance}
+veranschaulicht dabei, welche Fälle auftreten können und wie der rebalancierte Baum aussieht:
+
+\begin{figure}[h!]
+  \centering
+  \input{bilder/rbt_balance.tex}
+  \caption{Die potentiellen Verletzung der lokalen Invarianz nach dem Einfügen und wie sie ausbalanciert werden.}
+  \label{fig:rbt_balance}
+\end{figure}
+
+\paragraph{Suchen in (2-4)-Bäumen}
+
+Mit Hilfe der Hilfsinformationen in den inneren Knoten kann, von der Wurzel
+absteigend, der entsprechende Wert gesucht werden. Pro Knoten
+benötigt der Suchprozess eine konstante Zeit $\mathcal{O}(1)$. Die gesamte
+Laufzeit hängt also direkt von der Höhe des Baumes ab und ist damit $Θ(\log n)$.
+Wir starten dabei folgenden Algorithmus mit $\texttt{search\_24}(\texttt{root}(\mathcal{B}_{2,4}), v)$.
+
+\begin{algorithm}[h]
+  \SetNlSty{texttt}{[}{]}
+  \caption{\texttt{search\_24}($k, v$)}
+  \KwIn{A node $k$ and and a data value $v$.} 
+  \KwOut{The node $k'$ of the subtree of $k$ which has $\texttt{data}(k') = v$. an error, if there is no such $k'$.}
+  \eIf{$α(k) = 0$}{
+    \eIf{$\texttt{data}(k) = v$}{
+      \KwRet $k$ \;
+    }{
+      \texttt{error: value not found} \;
+    }
+  }{
+    \uIf{$\texttt{child}_3(k) \neq \texttt{void\_node}$ and $m_2 < v$}{
+      \KwRet $\texttt{search\_24}(\texttt{child}_3(k), v)$ \;
+    }
+    \uElseIf{$\texttt{child}_2(k) \neq \texttt{void\_node}$ and $m_1 < v$}{
+      \KwRet $\texttt{search\_24}(\texttt{child}_2(k), v)$ \;
+    }
+    \uElseIf{$m_0 < v$}{
+      \KwRet $\texttt{search\_24}(\texttt{child}_1(k), v)$ \;
+    }
+    \Else{
+      \KwRet $\texttt{search\_24}(\texttt{child}_0(k), v)$ \;
+    }
+  }
+\end{algorithm}
+
+
+\paragraph{Einfügen in (2-4)-Bäumen}
+
+Wollen wir in den Baum $\mathcal{B}_{2,4}$ ein Element einfügen, so müssen wir zuerst mittels
+$\texttt{search\_insert\_node}$ den richtigen inneren Knoten $k$ in der vorletzten Ebene suchen.
+Das geschieht analog zu $\texttt{search\_24}$, nur, dass wir statt dem Blatt bzw Fehler den entsprechenden
+Elternknoten inneren Knoten. 
+Dann fügen wir vermöge $\texttt{insert\_at\_node}(k,l)$ den neuen Knoten $l$ an der passenden Stelle bei $k$ ein (sodass
+die Reihenfolge nicht verletzt wird).
+Nun müssen wir zwei Fälle unterschieden:
+\begin{enumerate}
+  \item $\alpha(k)\leq 4$ nach dem Einfügen: Damit ist $\mathcal{B}_{2,4}$ weiterhin ein (2-4)-Baum.
+  \item$\alpha(k)=5$ nach dem Einfügen: Es liegt nun \emph{kein} (2,4)-Baum vor. Um den Fehler zu beheben, führen wir
+    $\texttt{split}$ aus. Dieser Algorithmus spaltet die zwei rechtesten Knoten von $k$ ab in einen neuen Knoten
+    $k'$, welcher rechts von $k$ am gleichen Elternknoten eingehängt wird. 
+    Dabei kann es vorkommen, dass auch der Elterknoten nun $5$ Kinder hat, weswegen wir unter Umständen
+    $\texttt{split}$ wiederholt aufrufen müssen, bis wir an der Wurzel sind. Muss auch die Wurzel aufgespalten werden,
+    wird eine neue Wurzel definiert, die dann die zwei Kinder $k$ und $k'$ hat.
+\end{enumerate}
+\begin{center}
+\includegraphics[scale=0.2]{bilder/Spalten}
+\par\end{center}
+In Pseudocode:
+
+\begin{algorithm}[h]
+  \SetNlSty{texttt}{[}{]}
+  \caption{\texttt{insert\_24}($\mathcal{B}_{2,4}, k, v$)}
+  \KwIn{A (2-4)-Tree $\mathcal{B}_{2,4}$, the intended target node $k$ and a new value $v$ to be inserted.} 
+  \KwOut{Side effects on the memory: $\mathcal{B}_{2,4}$ contains now $v$ in its correct place. Several splits of inner
+  nodes may happen to ensure (2-4)-integrity.}
+  $l = \text{new node with } \texttt{parent}(l) = k \text{ and } \texttt{data}(l) = v$ \;
+  $\texttt{insert\_at\_node}(k,l)$ \;
+  \uIf{$α(k) = 5$}{
+    $\texttt{split}(\mathcal{B}_{2,4}, k)$ \;
+  }
+\end{algorithm}
+\begin{algorithm}[h]
+  \SetNlSty{texttt}{[}{]}
+  \caption{\texttt{split}($\mathcal{B}_{2,4}, k$)}
+  \KwIn{An almost-(2-4)-Tree $\mathcal{B}_{2,4}$ which is violated in node $k$ (too many child nodes).} 
+  \KwOut{Side effects on the memory: $\mathcal{B}_{2,4}$ is modified by splitting the violating nodes recursivly. After
+  that, $\mathcal{B}_{2,4}$ is a flawless (2-4)-tree again.}
+  \uIf{$k = \texttt{root}(\mathcal{B}_{2,4})$}{
+    $r = \text{new node with } \texttt{child}_0(r) = k$ \;
+    $\texttt{parent}(k) \leftarrow r$ \;
+    $\texttt{root}(\mathcal{B}_{2,4}) \leftarrow r$ \;
+  }
+  $k' = \text{new node with } \texttt{parent}(k') = \texttt{parent}(k)$ \;
+  $\texttt{child}_0(k') \leftarrow \texttt{child}_3(k)$ \;
+  $\texttt{child}_3(k) \leftarrow \texttt{void\_node}$ \;
+  $\texttt{child}_1(k') \leftarrow \texttt{child}_4(k)$ \;
+  $\texttt{child}_4(k) \leftarrow \texttt{void\_node}$ \;
+  \texttt{insert\_at\_node}(\texttt{parent}(k), k') \;
+  \uIf{$α(\texttt{parent}(k)) = 5$}{
+    $\texttt{split}(\mathcal{B}_{2,4}, \texttt{parent}(k))$ \;
+  }
+\end{algorithm}
+
+Der Aufwand für \texttt{insert\_24} beträgt im besten Fall $\mathcal{O}(1)$ und im schlimmsten Fall
+$\mathcal{O}(\log n)$, falls wir rekursiv bis zur Wurzel spalten müssen.
+
+Man kann aber mittels aggregierter Analyse schnell zeigen, dass der amortisierte Aufwand fürs Einfügen nur
+$\mathcal{O}(1)$ beträgt.
+
+\subsubsection{Entfernen in (2-4)-Bäumen}
+
+Auch hier wird zuerst der entsprechende Knoten $k$ gesucht. Nach dem Entfernen
+muss man wieder zwei Fälle unterscheiden:
+\begin{enumerate}
+  \item Ist $\alpha(k)\geq 2$ nach dem Entfernen, so haben wir wieder einen (2-4)-Baum. 
+  \item Ist $\alpha(k)=1$ nach dem Entfernen: Es liegt nun \emph{kein} (2,4)-Baum mehr vor. Unser Knoten $k$ muss sich
+    nun Kinder von anderen Knoten besorgen. Es wird zuerst ein Knoten $k'$ ausgewählt, der ein direkter Bruder von
+    $k$ ist: Ist $k = \texttt{child}_i(\texttt{parent}(k))$, so ist $k' = \texttt{child}_{j}(\texttt{parent}(k))$ mit $j
+    \in \{i-1,i+1\}$, sofern vorhanden. Ein Bruderknoten muss immer vorhanden sein, da $\texttt{parent}(k)$ immernoch
+    der (2-4)-Bedinungen genügen muss und mindestens 2 Kinder hat.
+
+    Hat man $k'$ ausgewählt und hat $k'$ genug Kinder ($α(k') \geq 3$), so stiehlt man nun das nächstgrößere / nächstkleinere 
+    Blatt von $\texttt{child}_0(k)$ von $k'$ und hängt es bei $k$ ein. Damit hat $k$ wieder $2$ Kinder und der
+    (2-4)-Baum ist repariert.
+
+    Hat $k'$ allerdings selber nur $α(k')=2$ Kinder, so würde ein Stehlen die $(2-4)-Baum-Bedingung$ bei $k'$
+    verletzten. Deswegen verschmilzt man $k$ und $k'$ zu einem Knoten. Allerdings kann das, analog zu \texttt{split},
+    bewirken, dass $\texttt{parent}(k)$ nun auch zu wenig Knoten hat. Deswegen muss auch die verschmelz-Operation potentiell
+    $h \in Θ(\log n)$ mal aufgerufen werden.
+\end{enumerate}
+
+
+\begin{center}
+\includegraphics[scale=0.2]{bilder/Stehlen}
+\par\end{center}
+
+\begin{center}
+\includegraphics[scale=0.2]{bilder/Verschmelzen}
+\par\end{center}
+
+Aufgrund der vielen zu behandelnden Fallunterscheidungen verzichten wir hier auf Pseudocode.
+
+
+\subsection{Sortieren mit (2-4)-Bäumen}
+Eine primitive Methode des Sortierens mit (2-4)-Bäumen ist schnell gefunden. Wir nehmen unser zu sortierendes Array und
+fügen es von Anfang bis Ende in einen anfangs leeren (2-4)-Baum ein. Der Algorithmus ist worst-case-optimal, hat aber
+ansonsten keine weiteren erstrebenswerten Eigenschaften.
+
+In unserem Algorithmus \texttt{24treesort} gehen wir ein bisschen geschickter vor und erreichen dadurch Adaptivität. Sei
+also ein zu sortierendes Array $\mathcal{A} = [a_0, \dots, a_{n-1}]$ gegeben, welches wir nun von hinten nach vorne,
+also $a_{n-1}$ bis $a_0]$, in einen anfangs leeren (2-4)-Baum einfügen.
+
+Die Kernidee ist nun, die Suche nach dem passenden Einfügeort nicht von der Wurzel, sondern von dem bisher
+linkesten inneren Knoten $k$ zu beginnen! Ist $\mathcal{A}$ nun bereits sortiert, folgen in unserer umgekehrten
+Einfügereihenfolge also immer kleinere Elemente und unser $k$ ist direkt das richtige $k$ für \texttt{insert\_24}.
+
+Damit müssen wir nicht aufwendig den Einfügeort $k$ für \texttt{insert\_24} suchen, sondern können direkt einfügen, was
+einem amortisierter Aufwand von $\mathcal{O}(1)$ entspricht. Bei $n$ Einfügeoperationen haben wir im best-case also $\mathcal{O}(n)$ Aufwand.
+
+Haben wir nun aber ein Element $a_i$ mit Fehlstellung $f_i$, so müssen wir den Baum aufsteigen, bis wir zu einem
+passenden Knoten $k'$ kommen ($k'$ hat $m_0 > a_i$) und von dort wieder absteigen.
+Wie viele Knoten müssen wir aber hoch? Da alle Elemente $a_{n-1}$ bis $a_{i+1}$ bereits sortiert sind, müssen wir nur
+genau $f_i$ viele Elemente überspringen, also maximal $\log_2 f_i$ Knoten aufsteigen und wieder absteigen. Dies entspricht
+also dem Zusatzaufwand $\mathcal{O}(\log f_i)$ für jede Fehlstellung.
+
+Damit ist der Zeitaufwand für alle $n$ Elemente 
+\[
+  \mathcal{O}(n + \sum_{i=1}^n \log f_i) = \mathcal{O}(n + n \log \left( \frac{F}{n} \right)),
+\]
+wobei wir für die Umformung $\sum_{i=1}^n \log f_i = \log Π_{i=1}^n f_i \leq \log \left( \frac{F}{n} \right)^n = n \log
+\left( \frac{F}{n} \right)$ benutzen.
+
+Damit sind die Bedingungen für Adaptivität auch im strengeren Sinne erfüllt.
+
+TODO: Pseudocode für \texttt{24treesort}, falls es sich lohnt.
diff --git a/bilder/rbt_balance.tex b/bilder/rbt_balance.tex
new file mode 100644
index 0000000000000000000000000000000000000000..515b39b50c2f735af6dc8b6f24a413fd9c3207d2
--- /dev/null
+++ b/bilder/rbt_balance.tex
@@ -0,0 +1,69 @@
+\tikzsetnextfilename{rbt_balance}
+\begin{tikzpicture}[
+  treenode/.style = {align=center, inner sep=0pt, text centered, font=\sffamily},
+  node_black/.style = {treenode, circle, white, font=\sffamily\bfseries, draw=black, %TODO: Make font thick.
+    fill=black, text width=1.5em},% arbre rouge noir, noeud noir
+  node_red/.style = {treenode, circle, red, draw=red, 
+    text width=1.5em, very thick},% arbre rouge noir, noeud rouge
+    subtree/.style = {treenode, circle, text width=1.5em},
+  level/.style={sibling distance = 5cm/#1,
+  level distance = 1.5cm},
+  scale=0.4
+  ]
+  \begin{scope} % transformation target
+    \node [node_red] (ty) {$y$} 
+      child{ node [node_black] (tx) {$x$}
+        child{ node [subtree] (ta) {$a$}}
+        child{ node [subtree] (tb) {$b$}}}
+      child{ node [node_black] (tz) {$z$}
+        child{ node [subtree] (tc) {$c$}}
+        child{ node [subtree] (td) {$d$}}}
+    ;
+  \end{scope}
+
+  \begin{scope}[shift={(10,0)}] % case 1: right-right
+    \node [node_black] (c1x) {$x$} 
+      child{ node [subtree] (c1a) {$a$}}
+      child{ node [node_red] (c1y) {$y$}
+        child{ node [subtree] (c1b) {$b$}}
+        child{ node [node_red] (c1z) {$z$}
+          child{ node [subtree] (c1c) {$c$}}
+          child{ node [subtree] (c1d) {$d$}}}}
+    ;
+  \end{scope}
+
+  \begin{scope}[shift={(0,8.5)}] % case 2: left-right
+    \node [node_black] (c2z) {$z$} 
+      child{ node [node_red] (c2x) {$x$}
+        child{node [subtree] (c2a) {$a$}}
+        child{ node [node_red] (c2y) {$y$}
+          child {node [subtree] (c2c) {$c$}}
+        child {node [subtree] (c2b) {$b$}}}}
+      child{ node [subtree] (c2d) {$d$}}
+    ;
+  \end{scope}
+
+  \begin{scope}[shift={(-10,0)}] % case 3: left-left
+    \node [node_black] (c3z) {$z$} 
+      child{ node [node_red] (c3y) {$y$}
+        child{ node [node_red] (c3x) {$x$}
+          child {node [subtree] (c3a) {$a$}}
+          child {node [subtree] (c3b) {$b$}}}
+        child {node [subtree] (c3c) {$c$}}}
+      child{ node [subtree] (c3d) {$d$}}
+    ;
+  \end{scope}
+
+  \begin{scope}[shift={(0,-7.5)}] % case 4: right-left
+    \node [node_black] (c4x) {$x$} 
+      child{ node [subtree] (c4a) {$a$}}
+      child{ node [node_red] (c4z) {$z$}
+        child{ node [node_red] (c4y) {$y$}
+          child{ node [subtree] (c4b) {$b$}}
+          child{ node [subtree] (c4c) {$c$}}}
+        child{ node [subtree] (c4d) {$d$}}}
+    ;
+  \end{scope}
+
+
+\end{tikzpicture}