From: Martin Mares Date: Fri, 11 Apr 2008 18:20:49 +0000 (+0200) Subject: Fully dynamic connectivity. X-Git-Tag: printed~99 X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=04cdf2cffb5f1d6a9f2ffbc17778ed4aa7ab6388;p=saga.git Fully dynamic connectivity. --- diff --git a/dyn.tex b/dyn.tex index 318e7c3..8ed00ea 100644 --- a/dyn.tex +++ b/dyn.tex @@ -269,6 +269,7 @@ data structure. The structure consists of: in the leaves that represent active occurrences of original vertices. \:Boolean \df{markers} in the internal vertices that signal presence of a~non-tree edge anywhere in the subtree rooted at that internal vertex. +\:Counters $\(v)$ that contain the number of leaves in the subtree rooted at~$v$. \endlist \>The structure supports the following operations on the original trees: \itemize\ibull @@ -279,6 +280,7 @@ data structure. The structure consists of: \:$\(v)$ --- Return the root of the ET-tree containing the vertex~$v$. This can be used to test whether two vertices lie in the same tree. However, the root is not guaranteed to stay the same when the tree is modified by a~\ or \. +\:$\(v)$ --- Return the number of vertices in the tree containing the vertex~$v$. \:$\(v,e)$ --- Add a~non-tree edge~$e$ to the list at~$v$ and return a~unique identifier of this edge. \:$\(e)$ --- Delete a~non-tree edge~$e$ given by its identifier. @@ -302,19 +304,22 @@ which we remember. Joining of the roots translated to joining of the $(a,b)$-tre \ just follows parent pointers in the $(a,b)$-tree and it walks the path from the leaf to the root. +\ finds the root and returns its counter. + \ finds the leaf $\(v)$ containing the list of $v$'s non-tree edges and inserts the new edge there. The returned identifier will consist from the pointer to the edge and the vertex in whose list it is stored. Then we have to recalculate the markers -on the path from $\(v)$ to the root. \ is analogous. Whenever any -other operation changes a~vertex of the tree, it will also update its marker and, if necessary, -the markers on the path to the root. +on the path from $\(v)$ to the root. \ is analogous. + +Whenever any other operation changes a~vertex of the tree, it will also update its marker and +counter and, if necessary, the markers and counters on the path to the root. \ traverses the tree recursively from the root, but it does not enter the subtrees whose roots are not marked. Analysis of the time complexity is now straightforward: -\thmn{Eulerian Tour trees, Henzinger and Rauch \cite{henzinger:randdyn}} +\thmn{Eulerian Tour trees, Henzinger and Rauch \cite{henzinger:randdyn}}\id{etthm}% The ET-trees perform the operations \ and \ in time $\O(a\log_a n)$, \ in $\O(1)$, \, \, and \ in $\O(\log_a n)$, and \ in $\O(a\log_a n)$ per edge reported. Here $n$~is the number of vertices @@ -326,7 +331,7 @@ per operation on the ET-tree, plus $\O(1)$ other operations. We apply the standa on the complexity of $(a,b)$-trees \cite{clrs}. \qed -\examplen{Connectivity acceleration} +\examplen{Connectivity acceleration}\id{accel}% In most cases, the ET-trees are used with $a$~constant, but sometimes choosing~$a$ as a~function of~$n$ can also have its beauty. Suppose that there is a~data structure which maintains an~arbitrary spanning forest of a~dynamic graph. Suppose also that the structure works in time $\O(\log^k n)$ @@ -359,12 +364,100 @@ L = \lfloor\log_2 n\rfloor$. For each level~$i$, we will use~$F_i$ to denote the of~$F$ containing edges of level at least~$i$. Therefore $F=F_0 \supseteq F_1 \supseteq \ldots \supseteq F_L$. We will maintain the following \em{invariants:} -\numlist\ndotted +{\narrower +\def\iinv{{\bo I\the\itemcount~}} +\numlist\iinv \:$F$~is the maximum spanning forest of~$G$ with respect to the levels. In other words, - if $uv$ is a~non-tree edge, then $u$ and~$v$ are connected in~$F_{\ell(uw)}$. +if $uv$ is a~non-tree edge, then $u$ and~$v$ are connected in~$F_{\ell(uw)}$. \:For each~$i$, the components of~$F_i$ have at most $\lfloor n/2^i \rfloor$ vertices. - This implies that all~$F_i$ for $i>L$ are empty. +This implies that all~$F_i$ for $i>L$ are empty. \endlist +} + +At the beginning, the graph contains no edges, so both invariants are trivially +satistifed. Newly inserted edges can enter level~0, which cannot break I1 nor~I2. + +When we delete a~tree edge at level~$\ell$, we split a~tree~$T$ of~$F_\ell$ to two +trees $T_1$ and~$T_2$. Without loss of generality, let us assume that $T_1$ is the +smaller one. We will try to find the replacement edge of the highest possible +level that connects them back. From I1, we know that such an~edge cannot belong to +level greater than~$\ell$, so we start looking for it at level~$\ell$. According +to~I2, the tree~$T$ had at most $\lfloor n/2^\ell\rfloor$ vertices, so $T_1$ has +at most $\lfloor n/2^{\ell+1} \rfloor$ of them. Thus we can increase the levels +of all edges of~$T_1$ without violating either invariant. + +We now start enumerating the non-tree edges incident with~$T_1$. For each such edge, +we test whether its other endpoint lies in~$T_2$. If it does, we have found the replacement +edge and we insert it to~$F_\ell$. Otherwise we move the edge one level up. (This will +be the gist of our amortization argument: We can charge most of the work on level +increases and we know that the level of each edge can reach at most~$L$.) + +If the non-tree edges at level~$\ell$ are exhausted, we try the same in the next +lower level and so on. If there is no replacement edge on level~0, the tree~$T$ +remains disconnected. +\impl +We will use a~single ET-tree with~$a$ set to~2 for each level. For the $i$-th level, the ET-tree +${\cal E}_i$ will represent the forest~$F_i$ and the non-tree edges of +level~$i$ will be attached to its vertices. + +\algn{Insertion of an~edge} +\algo +\algin An~edge $uv$ to insert. +\:$\ell(uv) \= 0$. +\:Ask the ET-tree ${\cal E}_0$ if $u$ and~$v$ are in the same component. If they are: +\::Add $uv$ to the list of non-tree edges in ${\cal E}_0$ at both $u$ and~$v$. +\:Otherwise: +\::Add $uv$ to~$F_0$. +\endalgo + +\algn{Deletion of an~edge} +\algo +\algin An~edge $uv$ to delete. +\:$\ell \= \ell(uv)$. +\:If $uv$ is a~non-tree edge: +\::Remove $uv$ from the lists of non-tree edges at both $u$ and~$v$ in~${\cal E}_{\ell}$. +\:Otherwise: +\::Remove $uv$ from~$F_\ell$. +\::Call $\(uv,\ell)$. +\endalgo + +\algn{$\(uv,i)$ -- Search for an~replacement edge for~$uv$ at level~$i$} +\algo +\:Let $T_1$ and~$T_2$ be the trees in~$F_i$ containing $u$ and~$v$ respectively. +\:If $n(T_1) > n(T_2)$, swap $T_1$ with~$T_2$. +\:Increase levels of all edges in~$T_1$ to~$i+1$ and insert them to~${\cal E}_{i+1}$ + where needed. +\:Enumerate non-tree edges incident with vertices of~$T_1$ and stored in ${\cal E}_i$. + For each edge~$xy$, $x\in T_1$, do: +\::If $y\in T_2$, add the edge $xy$ to~$F_i$ and return. +\::Otherwise increase $\ell(xy)$ by one. This includes deleting~$xy$ from~${\cal E}_i$ + and inserting it to~${\cal E}_{i+1}$. +\:If $i>0$, call $\(xy,i-1)$. +\endalgo + +\>Analysis of the time complexity is straightforward: + +\thmn{Fully dynamic connectivity, Holm et al.~\cite{holm:polylog}} +Dynamic connectivity can be maintained in time $\O(\log^2 n)$ amortized for both +\ and \ and in time $\O(\log n/\log\log n)$ per \ +in the worst case. + +\proof +The direct cost of an~\ is $\O(\log n)$ for the operations on the ET-trees +(by Theorem \ref{etthm}). We will have it pre-pay all level increases of the new +edge. Since the levels never decrease, each edge can be brought a~level up at most +$L=\lfloor\log n\rfloor$ times. Every increase costs $\O(\log n)$ on the ET-tree +operations, so we pay $\O(\log^2 n)$ for all of them. + +A~\ costs $\O(\log^2 n)$ directly, as we might have to update all~$L$ +ET-trees. Additionally, we call \ up to $L$ times. The initialization of +\ costs $\O(\log n)$ per call, the rest is paid for by the edge level +increases. + +To bring the complexity of \ from $\O(\log n)$ down to $\O(\log n/\log\log n)$, +we apply the trick from Example \ref{accel} and store~$F_0$ in a~ET-tree with $a=\max(\lfloor\log n\rfloor,2)$. +This does not hurt the complexity of insertions and deletions, but allows for faster queries. +\qed \endpart