Skip to content
Snippets Groups Projects
Commit 6c71a067 authored by Florian Unger's avatar Florian Unger
Browse files

Kapitel 3.5 ohne Beweis amortisierte Analyse

parent f1b934a2
No related branches found
No related tags found
No related merge requests found
...@@ -34,6 +34,12 @@ Den Red-Black-Tree. ...@@ -34,6 +34,12 @@ Den Red-Black-Tree.
\end{itemize} \end{itemize}
\end{definition} \end{definition}
Wir notieren RBT über einen Datentyp $D$ analog zu BST und speichern lediglich zusätzlich die Farbe $R,B \in C$ im Node:
\begin{align*}
\text{RBT}\; D &= ε\ |\; C\quad \text{RBT}\; D\quad D\quad \text{RBT}\; D \\
\mathcal{B}\quad &= ε\ |\;\, c\qquad \ l\qquad \ \ \ x\; \ \qquad r
\end{align*}
Per Konvention nehmen wir an, dass der Wurzelknoten immer Schwarz ist. 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 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 Pfad dann durch $2l$ Knoten, immer einen Schwarzen und einen roten. Dadurch ist gewährleistet, dass das höchste Blatt
...@@ -55,178 +61,99 @@ Auch die grundsätzliche Idee des Einfügens bleibt gleich: Wie bei BST gehen wi ...@@ -55,178 +61,99 @@ Auch die grundsätzliche Idee des Einfügens bleibt gleich: Wie bei BST gehen wi
den passenden Platz für das neue Element. Eingefügte Elemente sind also erst einmal Blätter. 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 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} rot. Allerdings kann es dabei zu einer Verletzung der lokalen Invarianz kommen. Wie in Abbildung \ref{fig:rbt_balance}
veranschaulicht dabei, welche Fälle auftreten können und wie der rebalancierte Baum aussieht: skizziert, wird die lokale Invarianz durch eine \texttt{balance}-Operation wieder repariert. Es kann dabei sein, dass
die Verletzung dabei eine Ebene nach oben verschoben wird, also sind bis zu $h$ \texttt{balance}-Operationen notwendig.
Am Ende wird die Wurzel des Baums immer schwarz gefärbt.
\begin{figure}[h!] \begin{figure}[h!]
\centering \centering
\input{bilder/rbt_balance.tex} \input{bilder/rbt_balance.tex}
\caption{Die potentiellen Verletzung der lokalen Invarianz nach dem Einfügen und wie sie ausbalanciert werden.} \caption{Die potentiellen Verletzung der lokalen Invarianz nach dem Einfügen und wie sie ausbalanciert werden.
Potentiell leere Teilbäume werden durch $a, b, c$ und $d$ repräsentiert. Insbesondere direkt nach dem Einfügen des
neuen Elements sind $a,b,c,d=ε$.}
\label{fig:rbt_balance} \label{fig:rbt_balance}
\end{figure} \end{figure}
\paragraph{Suchen in (2-4)-Bäumen} Formaler wird der Algorithmus zum Einfügen von $x$ in den RBT $T$ wird durch die Funktion
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)), \texttt{insert}(T,x) = \texttt{makerootblack} \circ \texttt{ins}(B,x)
\] \]
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 beschrieben, wobei
\left( \frac{F}{n} \right)$ benutzen. \begin{align}
\texttt{makerootblack}(c\; l\; y\; r) &= (B\; l\; y\; r) \\
\texttt{ins}(ε,x) &= (R\; ε\; x\; ε) \\
\texttt{ins}((c\; l\; y\; r), x) &=
\begin{cases}
\texttt{balance} (c\; \texttt{ins}(l, x)\; y\; r) &\text{ falls } x < y, \\
\texttt{balance} (c\; l\; y\; \texttt{ins}(r, x)) &\text{ falls } x > y. \\
\end{cases}
\end{align}
Dabei macht \texttt{balance} nichts, solange nicht einer der vier Fälle aus Abbildung \ref{fig:rbt_balance} vorliegt:
\begin{align}
\begin{rcases}
\texttt{balance}(B\; a\; x\; (R\; b\; y\; (R\; c\; z\; d))) \\
\texttt{balance}(B\; (R\; a\; x\; (R\; b\; y\; c\;) z\; d)) \\
\texttt{balance}(B\; (R\; (R\; a\; x\; b)\; y\; c)\; z\; d) \\
\texttt{balance}(B\; a\; x\; (R\; (R\; b\; y\; c)\; z\; d)) \\
\end{rcases} &= (R\; (B\; a\; x\; b)\; y\; (B\; c\; x\; d)) \\
\texttt{balance}(t) &= t
\end{align}
Die vier Fälle von \texttt{balance} entsprechen den Graphen in Abbildung \ref{fig:rbt_balance} in der Reihenfolge rechts
oben, links oben, links unten und rechts unten.
\paragraph{Korrektheit}
Da der grundsätzliche Ort des Einfügens eines neues Elements (vor der \texttt{balance}-Operation) gleich zur einfacheren
Fall in binären Suchbäumen ist, muss zur Korrektheit lediglich überprüft werden, ob \texttt{balance}
\begin{itemize}
\item die Ordnung in $D$ respektiert (bezüglich der BST-Eigenschaft),
\item die globale Invarianz erhalten bleibt,
\item die lokale Invarianz wiederhergestellt wird.
\end{itemize}
Für den ersten Punkt hilft folgende Beobachtung: Alle vier Ausgangspositionen in Abbildung \ref{fig:rbt_balance} treffen
folgende Aussagen über die Ordnung in $D$: $a < x < b < y < c < z < d$, wobei wir mit $a,b,c,d$ hier streng genommen alle Elemente
aus den entsprechenden Teilbäumen meinen.
Genau diese Eigenschaft wird auch im balancierten Graphen repräsentiert.
Für den zweiten Punkt brauchen wir die Eigenschaft, dass vor dem Einfügen der RBT die globale Eigenschaft erfüllt hat,
also von der Wurzel zu jedem Blatt eine gleiche Anzahl schwarzer Knoten durchlaufen wurde. Diese Eigenschaft gilt dann
auch innerhalb eines jeden Teilbaums. Da durch das Einfügen nur ein roter Knoten eingefügt wurde, gilt die globale
Invarianz also auch in allen vier Ausgangsfällen, in denen \texttt{balance} den Baum modifiziert.
Betrachten wir nun den Fall oben rechts genauer. Von $x$ zu jedem Blatt durchschreitet der Pfad $k$ schwarze Knoten, erst
$x$ und dann $k-1$ schwarze Knoten in den Teilbäumen $a,b,c,d$.
Dadurch ändert sich nach dem Balancieren nichts - Wir starten vom Wurzelelement $y$, aber müssen auf dem Weg zu jedem
Blatt durch $x$ oder $z$ sowie die $k-1$ schwarzen Knoten der Teilbäume $a,b,c,d$.
Die anderen Fälle funktionieren analog.
Für den letzten Fall erkennen wir zuerst, dass die Teilbäume $a,b,c,d$ die lokale Invarianz erfüllt haben mussten, da der
Baum vor dem Einfügen sie erfüllte. Da $x$ und $z$ im balanzierten Baum schwarz sind, kann die Invarianz nach unten
nicht verletzt werden.
Allerdings kann es nach dem balancieren passieren, dass der Vaterknoten von $y$ auch rot ist und damit die lokale
Invarianz an einer anderen Stelle, eine Ebene höher verletzt ist. Das aber \texttt{balance} in den rekursiven Aufrufen
von \texttt{ins} mitaufgerufen wurde, wird auch diese neue Verletzung repariert.
Nur der Wurzelknoten stellt ein potentielles Problem dar. Daher wird er durch \texttt{makerootblack} schwarz eingefärbt,
was die globale Invarianz nicht weiter stört.
Somit arbeitet \texttt{insert} korrekt.
\paragraph{Laufzeit}
Die Laufzeit von \texttt{insert} ist immer $Θ(h)$, da allein das Suchen der richtigen Stelle so viel Zeit in Anspruch
nimmt.
Interessanter wird die Analyse, wenn man nur Schreibzugriffe betrachtet. In dem Fall ist der best-case
$\mathcal{O}(1)$, aber der worst-case Aufwand $\mathcal{O}(log n)$.
Dieses Ergebnis lässt sich noch verfeinern, da tatsächlich amortisiert $\mathcal{O}(1)$ Schreibaufwand garantiert werden
kann:
\begin{lemma}
Bei mehrfacher, sukzessiver Anwendung von \texttt{insert} passieren lediglich $\mathcal{O}(1)$ Schreibaufrufe, also
Anwendungen der Regeln (3.1), (3.3) und (3.5).
\label{lemma:rbt_amortisiert}
\end{lemma}
\begin{proof}
\end{proof}<++>
Damit sind die Bedingungen für Adaptivität auch im strengeren Sinne erfüllt.
TODO: Pseudocode für \texttt{24treesort}, falls es sich lohnt.
...@@ -33,6 +33,10 @@ ...@@ -33,6 +33,10 @@
\newcommand{\ld}{\mathrm{ld}} %remove once dependencies fixed \newcommand{\ld}{\mathrm{ld}} %remove once dependencies fixed
%\newenvironment{rcases}
% \ {\left.\begin{aligned}}
% {\end{aligned}\right\rbrace}
%pseudocode %pseudocode
\usepackage[linesnumbered,noend,noline,ruled]{algorithm2e} \usepackage[linesnumbered,noend,noline,ruled]{algorithm2e}
\SetEndCharOfAlgoLine{} \SetEndCharOfAlgoLine{}
...@@ -50,7 +54,7 @@ ...@@ -50,7 +54,7 @@
\usepgfplotslibrary{fillbetween} \usepgfplotslibrary{fillbetween}
\pgfplotsset{compat=1.15} \pgfplotsset{compat=1.15}
\usepackage{tikz} \usepackage{tikz}
\usetikzlibrary{arrows,calc,shadows,matrix,external,shapes.misc,positioning} \usetikzlibrary{arrows,calc,shadows,matrix,external,shapes.misc,positioning,snakes,topaths}
\usepgflibrary{shapes.misc} \usepgflibrary{shapes.misc}
%external figures - tikz %external figures - tikz
\tikzexternalize[prefix=bilder/] \tikzexternalize[prefix=bilder/]
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
level distance = 1.5cm}, level distance = 1.5cm},
scale=0.4 scale=0.4
] ]
\begin{scope} % transformation target \begin{scope} [local bounding box=target] % transformation target
\node [node_red] (ty) {$y$} \node [node_red] (ty) {$y$}
child{ node [node_black] (tx) {$x$} child{ node [node_black] (tx) {$x$}
child{ node [subtree] (ta) {$a$}} child{ node [subtree] (ta) {$a$}}
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
; ;
\end{scope} \end{scope}
\begin{scope}[shift={(10,0)}] % case 1: right-right \begin{scope}[shift={(8,7)}, local bounding box=case1] % case 1: right-right
\node [node_black] (c1x) {$x$} \node [node_black] (c1x) {$x$}
child{ node [subtree] (c1a) {$a$}} child{ node [subtree] (c1a) {$a$}}
child{ node [node_red] (c1y) {$y$} child{ node [node_red] (c1y) {$y$}
...@@ -32,18 +32,18 @@ ...@@ -32,18 +32,18 @@
; ;
\end{scope} \end{scope}
\begin{scope}[shift={(0,8.5)}] % case 2: left-right \begin{scope}[shift={(-8,7)}, local bounding box=case2] % case 2: left-right
\node [node_black] (c2z) {$z$} \node [node_black] (c2z) {$z$}
child{ node [node_red] (c2x) {$x$} child{ node [node_red] (c2x) {$x$}
child{node [subtree] (c2a) {$a$}} child{node [subtree] (c2a) {$a$}}
child{ node [node_red] (c2y) {$y$} child{ node [node_red] (c2y) {$y$}
child {node [subtree] (c2c) {$c$}} child {node [subtree] (c2b) {$b$}}
child {node [subtree] (c2b) {$b$}}}} child {node [subtree] (c2c) {$c$}}}}
child{ node [subtree] (c2d) {$d$}} child{ node [subtree] (c2d) {$d$}}
; ;
\end{scope} \end{scope}
\begin{scope}[shift={(-10,0)}] % case 3: left-left \begin{scope}[shift={(-8,-7.5)}, local bounding box=case3] % case 3: left-left
\node [node_black] (c3z) {$z$} \node [node_black] (c3z) {$z$}
child{ node [node_red] (c3y) {$y$} child{ node [node_red] (c3y) {$y$}
child{ node [node_red] (c3x) {$x$} child{ node [node_red] (c3x) {$x$}
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
; ;
\end{scope} \end{scope}
\begin{scope}[shift={(0,-7.5)}] % case 4: right-left \begin{scope}[shift={(8,-7.5)}, local bounding box=case4] % case 4: right-left
\node [node_black] (c4x) {$x$} \node [node_black] (c4x) {$x$}
child{ node [subtree] (c4a) {$a$}} child{ node [subtree] (c4a) {$a$}}
child{ node [node_red] (c4z) {$z$} child{ node [node_red] (c4z) {$z$}
...@@ -64,6 +64,13 @@ ...@@ -64,6 +64,13 @@
child{ node [subtree] (c4d) {$d$}}} child{ node [subtree] (c4d) {$d$}}}
; ;
\end{scope} \end{scope}
\draw [->,decorate,decoration={snake,amplitude=.3mm,segment length=2mm,post length=1mm}] (5.5,2.7) -- node[below right]{\tiny \texttt{balance}} (target);
\draw [->,decorate,decoration={snake,amplitude=.3mm,segment length=2mm,post length=1mm}] (-5.5,2.7) -- node[below left]{\tiny \texttt{balance}} (target);
\draw [->,decorate,decoration={snake,amplitude=.3mm,segment length=2mm,post length=1mm}] (case3) -- node[above left]{\tiny \texttt{balance}} (-3,-4.5);
\draw [->,decorate,decoration={snake,amplitude=.3mm,segment length=2mm,post length=1mm}] (case4) -- node[above right]{\tiny \texttt{balance}} (3,-4.5);
% todo: label cases
%\node [treenode, draw] {\tiny \text{I)}} (case1.north east);
%\node [treenode] {\tiny \text{II)}} (case2.north west);
\end{tikzpicture} \end{tikzpicture}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment