feat: start working on deletion
Signed-off-by: Matej Focko <mfocko@redhat.com>
This commit is contained in:
parent
77d569fa18
commit
1ead11e542
1 changed files with 67 additions and 18 deletions
|
@ -354,13 +354,13 @@ Implementation of the insertion is trivial, since it is described by \textit{Hae
|
||||||
When propagating the error, we can encounter 3 cases (we explain them with respect to propagating deletion from the left subtree, propagation from right is mirrored and role of trits $+$ and $-$ swaps):
|
When propagating the error, we can encounter 3 cases (we explain them with respect to propagating deletion from the left subtree, propagation from right is mirrored and role of trits $+$ and $-$ swaps):
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
\item \textit{Node was marked with $-$.} In this case, heights of left and right subtrees are equal now and node is marked with $0$, but propagation must be continued, since the height of the whole subtree has changed.
|
\item \textit{Node was marked with $-$.} In this case, heights of left and right subtrees are equal now and node is marked with $0$, but propagation must be continued, since the height of the whole subtree has changed.\label{avl:rules:delete:1}
|
||||||
\item \textit{Node was marked with $0$.} In this case, node is marked with $+$ and the height of the subrtree has not changed, therefore we can stop the propagation.
|
\item \textit{Node was marked with $0$.} In this case, node is marked with $+$ and the height of the subrtree has not changed, therefore we can stop the propagation.\label{avl:rules:delete:2}
|
||||||
\item \textit{Node was marked with $+$.} In this case, node would acquire balance-factor of $+2$, which is not allowed. In this situation we decide based on the mark of the node from which we are propagating the insertion in the following way (let $x$ the current node marked with $+$ and $y$ be the right child of $x$):
|
\item \textit{Node was marked with $+$.} In this case, node would acquire balance-factor of $+2$, which is not allowed. In this situation we decide based on the mark of the node from which we are propagating the insertion in the following way (let $x$ the current node marked with $+$ and $y$ be the right child of $x$):\label{avl:rules:delete:3}
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
\item $y$ is marked with $+$, then we rotate by $x$ to the left. After that both $x$ and $y$ can be marked with $0$. Height from the point of the parent has changed, so we continue the propagation.
|
\item $y$ is marked with $+$, then we rotate by $x$ to the left. After that both $x$ and $y$ can be marked with $0$. Height from the point of the parent has changed, so we continue the propagation.\label{avl:rules:delete:3a}
|
||||||
\item $y$ is marked with $0$, then we rotate by $x$ to the left. After the rotation, $x$ can be marked with $+$ and $y$ with $-$. Height of the subtree has not changed, so propagation can be stopped.
|
\item $y$ is marked with $0$, then we rotate by $x$ to the left. After the rotation, $x$ can be marked with $+$ and $y$ with $-$. Height of the subtree has not changed, so propagation can be stopped.\label{avl:rules:delete:3b}
|
||||||
\item $y$ is marked with $-$. Let $z$ be the left son of $y$. We double rotate: first by $z$ to the right and then by $x$ to the left. After the double-rotation $x$ can be marked by either $0$ or $-$, $y$ by $0$ or $+$ and $z$ gets $0$. Height of the subtree has changed, therefore we must propagate further.
|
\item $y$ is marked with $-$. Let $z$ be the left son of $y$. We double rotate: first by $z$ to the right and then by $x$ to the left. After the double-rotation $x$ can be marked by either $0$ or $-$, $y$ by $0$ or $+$ and $z$ gets $0$. Height of the subtree has changed, therefore we must propagate further.\label{avl:rules:delete:3c}
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
\end{enumerate}\label{avl:rules:delete}~\cite{labyrint}
|
\end{enumerate}\label{avl:rules:delete}~\cite{labyrint}
|
||||||
|
|
||||||
|
@ -547,24 +547,20 @@ Rebalancing after insertion in the WAVL tree is equivalent to rebalancing after
|
||||||
When propagating the error, we can encounter 3 cases (we explain them with respect to propagating insertion from the left subtree, propagation from right is mirrored and role of trits $+$ and $-$ swaps):
|
When propagating the error, we can encounter 3 cases (we explain them with respect to propagating insertion from the left subtree, propagation from right is mirrored and role of trits $+$ and $-$ swaps):
|
||||||
|
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
\item \textit{Node was marked with $+$.} In this case, heights of left and right subtrees are equal now and node is marked with $0$ and propagation can be stopped.
|
\item \textit{Node was marked with $+$.} In this case, heights of left and right subtrees are equal now and node is marked with $0$ and propagation can be stopped.\label{avl:rules:insert:1}
|
||||||
\item \textit{Node was marked with $0$.} In this case, node is marked with $-$, but the height of the tree rooted at the node has changes, which means that we need to propagate the changes further.
|
\item \textit{Node was marked with $0$.} In this case, node is marked with $-$, but the height of the tree rooted at the node has changes, which means that we need to propagate the changes further.\label{avl:rules:insert:2}
|
||||||
\item \textit{Node was marked with $-$.} In this case, node would acquire balance-factor of $-2$, which is not allowed. In this situation we decide based on the mark of the node from which we are propagating the insertion in the following way (let $y$ be the node from which the information is being propagated and $x$ the current node marked with $-$):
|
\item \textit{Node was marked with $-$.} In this case, node would acquire balance-factor of $-2$, which is not allowed. In this situation we decide based on the mark of the node from which we are propagating the insertion in the following way (let $x$ be the node from which the information is being propagated and $z$ the current node marked with $-$):\label{avl:rules:insert:3}
|
||||||
\begin{enumerate}
|
\begin{enumerate}
|
||||||
\item $y$ is marked with $-$, then we rotate by $x$ to the right. After that both $x$ and $y$ can be marked with $0$. Height from the point of the parent has not changed, so we can stop the propagation.
|
\item $x$ is marked with $-$, then we rotate by $z$ to the right. After that both $z$ and $x$ can be marked with $0$. Height from the point of the parent has not changed, so we can stop the propagation.\label{avl:rules:insert:3a}
|
||||||
\item $y$ is marked with $+$, then we double rotate: first by $y$ to the left and then by $x$ to the right. Here we need to recalculate the balance-factors for $x$ and $y$, where $x$ gets $-$ or $0$ and $y$ gets $0$ or $+$. Node that was a right child to the $y$ before the double-rotation is now marked with $0$ and propagation can be stopped.
|
\item $x$ is marked with $+$, then we double rotate: first by $x$ to the left and then by $z$ to the right. Here we need to recalculate the balance-factors for $z$ and $x$, where $z$ gets $-$ or $0$ and $x$ gets $0$ or $+$. Node that was a right child to the $x$ before the double-rotation is now marked with $0$ and propagation can be stopped.\label{avl:rules:insert:3b}
|
||||||
\item $y$ is marked with $0$. This case is trivial, since it cannot happen, because we never propagate the height change from a node that acquired sign $0$.
|
\item $x$ is marked with $0$. This case is trivial, since it cannot happen, because we never propagate the height change from a node that acquired sign $0$.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
\end{enumerate}~\cite{labyrint}
|
\end{enumerate}~\cite{labyrint}
|
||||||
|
|
||||||
|
In the following explanation we have to consider that valid nodes in AVL tree implemented via ranks are (1, 1) and (1, 2) and by the time of evaluating rank-differences of parent, they are already affected by the rebalancing done from the inserted leaf.
|
||||||
|
|
||||||
Rebalancing of the tree is equivalent to rebalancing of AVL tree and is executed in a following way:
|
Rebalancing of the tree is equivalent to rebalancing of AVL tree and is executed in a following way:
|
||||||
|
|
||||||
As a first step, it is checked whether parent of a current (inserted) node is (0, 1) or (1, 0)-node, i.e. parent node was leaf node before. In that case parent node's rank is promoted and the process continues by parent node becoming the current node. (After the initial check we can encounter such nodes as a result of promoting the parent.)
|
|
||||||
|
|
||||||
Once the node does not have a parent (is root node) or is not (0, 1) or (1, 0) node, we might end up in a situation where the rank rule does not hold \textbf{and} we cannot fix it with promotion.
|
|
||||||
|
|
||||||
Bottom-up rebalancing after the insertion into WAVL tree is identical to AVL tree insertion:
|
|
||||||
|
|
||||||
\begin{algorithm}
|
\begin{algorithm}
|
||||||
\Proc{$\texttt{insertRebalance}(T, node)$}{
|
\Proc{$\texttt{insertRebalance}(T, node)$}{
|
||||||
\tcp{Handles \hyperref[avl:rules:insert:2]{rule 2}}
|
\tcp{Handles \hyperref[avl:rules:insert:2]{rule 2}}
|
||||||
|
@ -587,6 +583,17 @@ Bottom-up rebalancing after the insertion into WAVL tree is identical to AVL tre
|
||||||
\caption{Algorithm containing bottom-up rebalancing after insertion}\label{algorithm:wavl:insertRebalance}
|
\caption{Algorithm containing bottom-up rebalancing after insertion}\label{algorithm:wavl:insertRebalance}
|
||||||
\end{algorithm}
|
\end{algorithm}
|
||||||
|
|
||||||
|
As a first step, which can be seen in \autoref{algorithm:wavl:insertRebalance}, we iteratively check rank-differences of a parent of the current node. As long as it is a (0, 1) or (1, 0) node, we promote it and propagate further. There is an interesting observation to be made about the way \textit{how parent can fulfill such requirement}. And the answer is simple, since we are adding a leaf or are already propagating the change to the root, it means that we have lowered the rank-difference of the parent, therefore it must have been (1, 1) node. From the algorithm used for usual implementations of AVL trees, this step refers to \hyperref[avl:rules:insert:2]{\textit{rule 2}}. After the promotion the rank of the parent becomes (1, 2) or (2, 1) which means that it gets sign $-$ (or $+$ respectively when propagating from the right subtree), which conforms to the usual algorithm.
|
||||||
|
|
||||||
|
After this, we might end up in two situations and those are:
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item Current node is not a 0-child, which means that after propagation and promotions we have gotten to a parent node that is (1, 2) or (2, 1), which refers to the \hyperref[avl:rules:insert:1]{\textit{rule 1}}.
|
||||||
|
\item Current node is a 0-child, which means that after propagation and promotions we have a node with a parent that is either (0, 2) or (2, 0) node. This case conforms to the \hyperref[avl:rules:insert:3]{\textit{rule 3}} and must be handled further to fix the broken rank rule.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\hyperref[avl:rules:insert:3]{\textit{Rule 3}} is then handled by a helper function that can be seen in \autoref{algorithm:wavl:fix0Child}.
|
||||||
|
|
||||||
\begin{algorithm}
|
\begin{algorithm}
|
||||||
\Proc{$\texttt{fix0Child}(T, x, y, rotateToLeft, rotateToRight)$}{
|
\Proc{$\texttt{fix0Child}(T, x, y, rotateToLeft, rotateToRight)$}{
|
||||||
$z \gets x.parent$\;
|
$z \gets x.parent$\;
|
||||||
|
@ -608,6 +615,8 @@ Bottom-up rebalancing after the insertion into WAVL tree is identical to AVL tre
|
||||||
\caption{Generic algorithm for fixing 0-child after insertion}\label{algorithm:wavl:fix0Child}
|
\caption{Generic algorithm for fixing 0-child after insertion}\label{algorithm:wavl:fix0Child}
|
||||||
\end{algorithm}
|
\end{algorithm}
|
||||||
|
|
||||||
|
Here we can see, once again, an interesting pattern. When comparing to the algorithm described above, using the rank representation, we do not need to worry about changing the signs and updating the heights, since by rotating combined with demotion and promotion of the ranks, we are effectively updating the height (represented via rank) of the affected nodes. This observation could be used in \autoref{algorithm:avl:deleteFixNode} and \autoref{algorithm:avl:deleteRotate} where we turned to manual updating of ranks to show the difference.
|
||||||
|
|
||||||
\section{Deletion from the Weak AVL Tree}
|
\section{Deletion from the Weak AVL Tree}
|
||||||
|
|
||||||
\begin{algorithm}
|
\begin{algorithm}
|
||||||
|
@ -633,6 +642,46 @@ Bottom-up rebalancing after the insertion into WAVL tree is identical to AVL tre
|
||||||
\caption{Initial phase of algorithm for the rebalance after deletion from the WAVL tree}\label{algorithm:wavl:deleteRebalance}
|
\caption{Initial phase of algorithm for the rebalance after deletion from the WAVL tree}\label{algorithm:wavl:deleteRebalance}
|
||||||
\end{algorithm}
|
\end{algorithm}
|
||||||
|
|
||||||
|
As described by \textit{Haeupler et al.}, we start the deletion rebalancing by checking for (2, 2) node. If that is the case, we demote it and continue with the deletion rebalancing via \autoref{algorithm:wavl:bottomUpDelete} if we have created a 3-child by the demotion. Demoting the (2, 2) node is imperative, since it enforces part of the \textit{Weak AVL Rule} requiring that leaves have rank equal to zero.
|
||||||
|
|
||||||
|
For example consider the following tree in \autoref{fig:wavl:twoElements}. Deletion of key 2 from that tree would result in having only key 1 in the tree with rank equal to 1, which would be (2, 2) node and leaf at the same time. After the demotion of the remaining key, we acquire the tree as shown in \autoref{fig:wavl:twoElementsAfterDelete}
|
||||||
|
|
||||||
|
In contrast to the \textit{AVL Rule}, WAVL tree allows us to have (2, 2) nodes present. Therefore we can encounter two key differences during deletion rebalancing:
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item If anywhere during the deletion rebalancing, \textbf{but not} at the start, we encounter (2, 2) node, we can safely stop the rebalancing process, since rest of the tree must be correct and we have fixed errors on the way to the current node from the leaf.
|
||||||
|
\item Compared to the AVL tree, during deletion rebalancing we need to fix \textbf{3-child} nodes.
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\begin{figure}
|
||||||
|
\centering
|
||||||
|
\begin{tikzpicture}[>=latex',line join=bevel,scale=0.75,]
|
||||||
|
%%
|
||||||
|
\node (Node{value=1+ rank=1}) at (28.597bp,105.0bp) [draw,ellipse] {1, 1};
|
||||||
|
\node (Node{value=2+ rank=0}) at (28.597bp,18.0bp) [draw,ellipse] {2, 0};
|
||||||
|
\draw [->] (Node{value=1+ rank=1}) ..controls (28.597bp,75.163bp) and (28.597bp,59.548bp) .. (Node{value=2+ rank=0});
|
||||||
|
\definecolor{strokecol}{rgb}{0.0,0.0,0.0};
|
||||||
|
\pgfsetstrokecolor{strokecol}
|
||||||
|
\draw (33.597bp,61.5bp) node {1};
|
||||||
|
%
|
||||||
|
\end{tikzpicture}
|
||||||
|
\caption{WAVL tree containing two elements}
|
||||||
|
\label{fig:wavl:twoElements}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
|
\begin{figure}
|
||||||
|
\centering
|
||||||
|
\begin{tikzpicture}[>=latex',line join=bevel,scale=0.75,]
|
||||||
|
%%
|
||||||
|
\node (Node{value=1+ rank=1}) at (28.597bp,105.0bp) [draw,ellipse] {1, 0};
|
||||||
|
\definecolor{strokecol}{rgb}{0.0,0.0,0.0};
|
||||||
|
\pgfsetstrokecolor{strokecol}
|
||||||
|
%
|
||||||
|
\end{tikzpicture}
|
||||||
|
\caption{\autoref{fig:wavl:twoElements} after deletion of 2}
|
||||||
|
\label{fig:wavl:twoElementsAfterDelete}
|
||||||
|
\end{figure}
|
||||||
|
|
||||||
\begin{algorithm}
|
\begin{algorithm}
|
||||||
\Proc{$\texttt{bottomUpDelete}(T, x, parent)$}{
|
\Proc{$\texttt{bottomUpDelete}(T, x, parent)$}{
|
||||||
\If{$x \text{ is not 3-child} \lor parent = nil$}{
|
\If{$x \text{ is not 3-child} \lor parent = nil$}{
|
||||||
|
|
Reference in a new issue