Space-Efficient Vertex Separators for Treewidth
Frank Kammer, Johannes Meintrup, Andrej Sajenko

TL;DR
This paper introduces a space-efficient algorithm for computing vertex separators and tree decompositions in graphs with bounded treewidth, enabling solutions to several problems using minimal memory.
Contribution
It presents the first space-efficient algorithms for tree decomposition and related problems in graphs with certain treewidth bounds, using only O(n) bits.
Findings
Algorithm computes vertex separators with O(n) bits.
Provides an O(1)-approximation for tree decomposition.
Solves Vertex Cover, Independent Set, and other problems with limited memory.
Abstract
For -vertex graphs with treewidth and an arbitrary , we present a word-RAM algorithm to compute vertex separators using only bits of working memory. As an application of our algorithm, we give an -approximation algorithm for tree decomposition. Our algorithm computes a tree decomposition in time using bits for some constant . We finally use the tree decomposition obtained by our algorithm to solve Vertex Cover, Independent Set, Dominating Set, MaxCut and -Coloring by using bits as long as the treewidth of the graph is smaller than for some problem dependent constant .
Click any figure to enlarge with its caption.
Figure 1
Figure 2
Figure 3Peer Reviews
No public reviews on file for this paper yet. If you reviewed it on a platform where reviews are public (OpenReview, ICLR, NeurIPS, ICML), you can paste yours below so the community can read it here.
Videos
No videos yet. Explain this paper in a talk, walkthrough, or lecture? Add one.
Taxonomy
TopicsAdvanced Graph Theory Research · Complexity and Algorithms in Graphs · Algorithms and Data Compression
\hideLIPIcs
THM, University of Applied Sciences Mittelhessen, Giessen, [email protected]://orcid.org/0000-0002-2662-3471 THM, University of Applied Sciences Mittelhessen, Giessen, [email protected]://orcid.org/0000-0003-4001-1153
THM, University of Applied Sciences Mittelhessen, Giessen, Germany [email protected]://orcid.org/0000-0001-5946-8087 Funded by the Deutsche Forschungsgemeinschaft (DFG, German Research Foundation) – 379157101.
\CopyrightFrank Kammer, Johannes Meintrup and Andrej Sajenko \ccsdesc[500]Mathematics of computing Graph algorithms
Acknowledgements.
We thank the anonymous reviewers for their careful reading of our manuscript and their helpful comments.\EventEditors \EventNoEds0 \EventLongTitle \EventShortTitle \EventAcronym \EventYear \EventDate \EventLocation \EventLogo \SeriesVolume \ArticleNo
Space-Efficient Vertex Separators for Treewidth
Frank Kammer
Johannes Meintrup
Andrej Sajenko
Abstract
For -vertex graphs with treewidth and an arbitrary , we present a word-RAM algorithm to compute vertex separators using only bits of working memory. As an application of our algorithm, we give an -approximation algorithm for tree decomposition. Our algorithm computes a tree decomposition in time using bits for some constant .
We finally use the tree decomposition obtained by our algorithm to solve Vertex Cover, Independent Set, Dominating Set, MaxCut and -Coloring by using bits as long as the treewidth of the graph is smaller than for some problem dependent constant .
keywords:
FPT, tree decomposition, network flow, subgraph stack
1 Introduction
For solving problems in the context of the ever-growing field of big data we require algorithms and data structures that do not only focus on runtime efficiency, but consider space as an expensive and valuable resource. Some reasons for saving memory are that slower memory in the memory hierarchy has to be used, less cache faults arise and the available memory allows us to run more parallel tasks on a given problem.
As a solution researchers began to provide space-efficient algorithms and data structures to solve basic problems like graph-connectivity problems [3, 5, 14, 19, 23], other graph problems [26, 31], memory initialization [25, 32], dictionaries with constant-time operations [12, 15, 22] or graph interfaces [6, 26] space efficiently, i.e., they designed practical algorithms and data-structures that run (almost) as fast as standard solutions for the problem under consideration while using asymptotically less space. Several space-efficient algorithms and data structures mentioned above are implemented in an open source GitHub project [30].
Our model of computation is the word RAM where we assume to have the standard operations to read, write as well as arithmetic operations (addition, subtraction, multiplication and bit shift) take constant time on a word of size bits where is the size of the input. To measure the total amount of memory that an algorithm requires we distinguish between the input memory, i.e., the read-only memory that stores the input, and the working memory, i.e., the read-write memory an algorithm additionally occupies during the computation. In contrast, the streaming model [34] allows us to access the input only in a purely sequential fashion, and the main goal is to minimize the number of passes over the input. There are several algorithms for NP-hard problems on a streaming model, e.g., a kernelization algorithm due to Fafianie and Kratsch [20]. Elberfeld, Jakoby, and Tantau [18] proved that the problem of obtaining a so-called tree decomposition is contained in LSPACE.
We continue the research of Banerjee, Chakraborty, Raman, Roy and Saurabh [4] on space-efficient algorithm on the word RAM for NP-hard problems, but they assumed that a tree decomposition is already given with the input.
One approach to solve NP-hard graph problems is to decompose the given graph into a tree decomposition consisting of a tree where each node of the tree has a bag containing a small fraction of the original vertices of the graph. The quality of a tree decomposition is measured by its width, i.e., the number of vertices of the largest bag minus 1. The treewidth of a graph is the smallest width over all tree decompositions for the graph. Having a tree decomposition of a graph, the problem is solved by first determining the solution size of the problem in a bottom-up traversal on and second in a top-down process computing a solution for the whole graph. Treewidth is also a topic in current interdisciplinary research, such as smart contracts using cryptocurrency [13] or computational quantum physics [17], which are fields that often work with big data sets. So it is important to have space efficient algorithms.
Several algorithms are known for computing a tree decomposition. For the following, we assume that the given graphs have vertices and treewidth . Based on ideas in [38], Reed [36] showed an algorithm for computing a tree decomposition of width in time for some constant . His algorithm repeatedly uses a so-called balanced separator that splits the input graph into roughly equally sized subgraphs, each used as an input for a recursive call of the algorithm. Further tree-decomposition algorithms can be found in [2, 8, 11, 21, 33]. Recently, Izumi and Otachi [27] presented an algorithm that runs with bits, but time on graphs with treewidth for an arbitrary . The basic strategy of repeated separator searches is the foundation of all treewidth approximation algorithms, as mentioned by Bodlaender et al. [10]. Using the same strategy, Bodlaender et al. also presented an algorithm that runs in time and finds a tree decomposition having width .
To obtain a space-efficient approximation algorithm for treewidth we modify Reed’s algorithm. We finally use a hybrid approach, which combines our new algorithm and Bodlaender et al.’s algorithm [10] to find a tree decomposition in time for some constant . The general idea for the hybrid approach is to use our space-efficient algorithm for treewidth only for constructing the nodes of height at most . For the subgraph induces by the bags of the vertices below a node of height , we use Bodlaender et al.’s algorithm. The most computationally difficult task of this paper is the computation of the separators with bits.
Finding separators requires finding vertex-disjoint paths for which a DFS as a subroutine is usually used. All recent space-efficient DFS require bits [3, 14, 19, 23] or time due to separator searches [27]. Thus, our challenge is to compute a separator and subsequently a tree decomposition with bits with a running time almost linear in .
To compute a separator of size at most with bits, the idea is to store up to vertex disjoint paths by assigning a color to each vertex such that we know to which path belongs. We also number the vertices along a path with etc. so that we know the direction of the path. Since we want to find separators with only bits, we further show that it suffices to store the color information only at every th vertex. We so manage to find separators of size at most with bits. If for an arbitrary , we thus use bits.
Our solution to find a separator is in particular interesting because previous space-efficient graph-traversal algorithms either reduce the space from bits to , e.g., depth first search (DFS) or breath first search (BFS) [14, 19, 23], or reduce the space from to , e.g., Euler partition [26] and cut-vertices [28]. In contrast, we reduce the space for the separator search from bits to bits for small treewidth .
Besides the separator search, many algorithms for treewidth store large subgraphs of the -vertex, -edge input graph during recursive calls, i.e., they require bits. We modify and extend the algorithm presented by Reed with space-efficiency techniques (e.g., store recursive graph instances with the so-called subgraph stack [26]) to present an iterator that allows us to output the bags of a tree decomposition of width in an Euler-traversal order using bits in time for some constant . To lower the space bound further, we use the subgraph stack only to store the vertices of the recursive graph instances. For the edges we present a new problem specific solution.
In Section 2, we summarize known data structures and algorithms that we use afterwards. Our main result, the computation of -vertex disjoint paths is shown in Section 3. We sketch Reed’s algorithm in Section 4, where we also show a space-efficient computation of a balanced vertex separator using bits. In Section 5 we present an iterator that outputs the bags of a tree decomposition using bits. In the following section we lower the space bound to bits for small treewidth, and show our hybrid approach. We conclude the paper by showing in Section 8 that our tree decomposition iterator can be used to solve NP-hard problems like Vertex Cover, Independent Set, Dominating Set, MaxCut and -Coloring with bits on graphs with small treewidth. The table below summarizes the space bound of the algorithms described in this paper.
2 Preliminaries
Let be an undirected -vertex -edge graph. If it is helpful, we consider an edge as two directed edges (called arcs) and . As usual for graph algorithms we define . For every vertex , we access the in- and out-degree of through functions that returns the number of incoming and outgoing edges of vertex , respectively. Moreover, . We denote by the neighborhood of . For , let be a function such that returns the th neighbor of . Intuitively speaking, each vertex has an adjacency array. If space is not a concern, it is customary to store each graph that results from a transformation separately. To save space, we always use the given graph and store only some auxiliary information that helps us to implement the following graph interface for a transformed graph.
Definition 2.1**.**
(graph interface) A data structure for a graph implements the graph interface (with adjacency arrays) exactly if it provides the functions , , where , and gives access to the number of vertices and the number of edges.
Let be a graph and let . During our computation, some of our graph interfaces can support and only for vertices . For vertices in , we can access their neighbors via adjacency lists, i.e., we can use the functions , and for a set of pointers to output the neighbors of a vertex as follows: ; { ; ; }. We then say that we have a graph interface with access-restricted vertices.
In an undirected graph it is common to store an edge at both endpoints, hence, every undirected edge is stored as an arc (directed edge) at the endpoint and as an arc at the endpoint . We also use the two space-efficient data structures below.
Definition 2.2**.**
(subgraph stack [26, Theorem 4.7]) The subgraph stack is a data structure initialized for an -vertex -edge undirected graph that manages a finite list , called the client list, of ordered graphs such that is a proper subgraph of for .
Each graph in the client list implements the graph-access interface supporting the access operations from above in time. Additionally, it provides the following operations:
- •
: Appends a new graph to the client list. The graph is represented by two bit arrays and , with , exactly if the vertex (or arc) of is still contained in . It runs in .
- •
: Removes from the client list in constant time.
- •
: Subsequent accesses to the interface of can run in time. Runs in time and uses bits additional space.
For the special case where we have a constant with such that and for all , the entire subgraph stack uses bits of space. We will use the subgraph stack only in this special case with .
Using fast rank-select data structures [7] one can easily support the following data structure.
Definition 2.3**.**
(static space allocation) For , let be the universe and a fixed set of keys with for some . Assume that the keys are stored within an array of bits so that a word RAM can read in time. Then there is a data structure using bits that allows us to read and write bits in constant time for each and the initialization of the data structure can be done in time.
We now formally define a tree decomposition.
Definition 2.4**.**
*(tree decomposition [37], bag) A tree decomposition of a graph is a pair where is a tree and is a mapping such that the following properties hold: **(**TD1) , and **(*TD2) for each vertex , the nodes with induce a subtree in . For each node , is called the bag of .
We recall from the introduction that the width of a tree decomposition is defined as the number of vertices in a largest bag minus 1 and the treewidth of a graph as the minimum width among all possible tree decompositions for . We subsequently also use the well-known fact that an -vertex graph with treewidth has edges [37].
Our algorithms use space-efficient BFS and DFS.
Theorem 2.5**.**
(BFS [19]) Given an -vertex -edge graph , there is a BFS on that runs in time and uses bits.
By taking the randomized DFS of Choudhari et al. [14] and replacing a randomized dictionary [14, Lemma 3] by Hagerup’s dictionary [23, Corollary 5.3], we obtain the following.
Theorem 2.6**.**
Given an -vertex -edge graph , there is a DFS on that runs in time and uses bits.
The DFS of Theorem 2.6 assumes that we provide a graph interface with adjacency arrays. Later we run a DFS on an -vertex graph with treewidth , but can only provide a graph interface with access-restricted vertices, i.e., vertices are provided by a list interface.
Lemma 2.7**.**
Assume that a graph interface for an -vertex, -edge graph with access-restricted vertices for some is given. Assume further that we can iterate over the adjacency list of an access-restricted vertex in time for some function , whereas we can access an entry in the adjacency array of another vertex still in time. Then, there is a DFS that runs in time and uses bits.
Proof 2.8**.**
*To get an idea consider first a standard DFS. Basically it starts from some vertex , visits each vertex of the connected component of , and iterates over the incident edges of each visited vertex once. If the DFS visits a vertex , then the current - path is managed in a stack that stores the status of the current iteration over the incident edges of each vertex on the path, i.e., for each vertex on the path, it stores the index of the edges of the path such that the DFS can return to a vertex and proceed with the next edge to explore another path. The space-efficient DFS (Theorem 2.6) stores only a small part of the entire DFS stack and uses further stacks with approximations of the index pointers to reconstruct removed information from the stack. The reason for storing only small parts of the stack as well as approximate indices is to bound the space-usage by bits. Let be the set of access-restricted vertices with . For the access restricted vertices we are not able to use this strategy, since we only provide a list interface i.e, we are unable to use indices. Thus, for the vertices of we simply ignore the space restrictions imposed by the storage scheme of the DFS and instead store a pointer with bits into the adjacency list for each vertex additionally to the storage scheme already in place for the DFS, just as any regular DFS working with adjacency lists would need. This of course uses extra space and thus results in the space-efficient DFS using bits instead of bits. Since there is one iteration over the incident edges of each of the access-restricted vertices, the running time of the DFS increases to time. *
3 Finding Vertex-Disjoint Paths using
Bits
To compute vertex-disjoint --paths we modify a standard network-flow technique where a so-called residual network [1] and a standard DFS is used.
Lemma 3.1**.**
(Network-Flow Technique [1]) Given an integer as well as an -vertex -edge graph with pairwise internal vertex-disjoint --paths for some vertices , there is an algorithm that can compute internal pairwise vertex-disjoint - paths using bits by executing times a depth-first search, i.e., in time.
The well-known network-flow technique increases the size of an initially empty set of internal vertex-disjoint --paths one by one in rounds. It makes usage of (1) a so-called residual network for extending a set of edge-disjoint paths by another edge disjoint path and (2) a simple reduction that allows us to find vertex-disjoint paths by constructing edge-disjoint paths in a modified graph. We can neither afford storing any copy of a the original graph, a graph transformated nor the exact routing of several paths because it would take bits. Instead, we use graph interfaces that allow access to a modified graph on the fly and we partition the paths in parts belonging to small so-called regions that allow us to store exact path membership on the boundaries and recompute the paths in between the boundaries of the regions on the fly if required. For a sketch of the idea, see Fig.1. Storing the exact path membership only for the boundaries has the drawback that we cannot recompute the original paths inside a region, but using a deterministic algorithm we manage to compute always the same one. It is important for us to remark that the network-flow technique above also works even if we change the exact routing of the --paths (but not their number) before or after each round.
For the rest of this section, let be an -vertex -edge graph with treewidth such that has internal pairwise vertex-disjoint --paths for some vertices and let be a set of pairwise internally vertex-disjoint --paths. Let be the set that consists of the union of all vertices over all paths in .
Before we can present our algorithm, we present two lemmas that consider properties of internal pairwise vertex-disjoint paths as given in . To describe the properties we first define a special path structure (with respect to ). A deadlock cycle in with respect to consists of a sequence of paths in for some such that there is a subpath on every path () and there is an edge . We call a deadlock cycle simple if every subpath consists of exactly two vertices, and otherwise extended (see Fig. 2). Moreover, we call an --path chordless if only subsequent vertices of the path are connected by an edge called chord. In some sense, a chord of a path is a deadlock cycle over only one path.
Lemma 3.2**.**
Assume that the paths in are chordless and that has no extended deadlock cycle with respect to . Any other set of internal pairwise vertex-disjoint --paths in uses all vertices of .
Proof 3.3**.**
Note that each path in uses another neighbor of and since the paths are chordless, can not have another neighbor in . In other words, has exactly neighbors in and each path in must use exactly one of these neighbors. This allows us to name the paths in and in with and , respectively, such that the second vertex of and are the same for all .
The following description assumes suitable indices of the paths in . Assume for a contradiction that the lemma is not true. This means that uses a vertex whereas leaves the vertices of at some vertex to avoid using a vertex . Since the paths are chordless, must jump on a vertex of a path . Since the paths are chordless, this means that must leave to a path . At the end, there must be a path () that jumps to a vertex on . If is before (while walking on from to ), then we rename the indices of the paths in such that the paths with the same index in and in use the same vertex strictly after the jumps in . Afterwards, we can reapply the paragraph. If is behind , we have an extended deadlock cycle in with respect to ; a contradiction.
Lemma 3.4**.**
Assume that the paths in are chordless and that has no extended deadlock cycle with respect to . Any other set of internal pairwise vertex-disjoint --paths in is chordless and has no extended deadlock cycle with respect to .
Proof 3.5**.**
For a contradiction assume that has a path with a chord such that some vertex is in between and on the path . This allows us to construct a path from that uses the edge instead of the subpath of between and , leaving unused. We so get paths that does not use , which is a contradiction to Lemma 3.2.
Assume now that there is an extended deadlock cycle in with respect to and some vertex on such that is in the middle of one of the subpaths of . By removing the common edges of and the paths in from the paths in and adding the remaining edges of to the paths in , we get a set of pairwise vertex-disjoint --paths in . An example is sketched in Fig. 3. The existence of is again a contraction to Lemma 3.2.
We begin with a high-level description of our approach to compute space efficiently. Afterwards, we present the missing details. A chordless --path can be computed by a slightly extended DFS and to identify the path it suffices to store its vertices in an -bit array (Lemma 3.6). However, if we want to store chordless pairwise internally vertex-disjoint --paths in , we cannot distinguish them without further information. For an easier description assume that each path with its vertices is colored with a different color. Using an array of fields of bits each we can easily store the coloring of each vertex and thus chordless paths with bits in total. However, our goal is to store paths with bits for some polynomial function . We therefore define a so-called path data scheme that stores partial information about the paths using bits (Def. 3.8). The idea of our scheme is to select and color a set of vertices along the paths with the property that and consists only of small connected components of vertices. We later call these components regions.
Such a set exists if the paths have so-called good properties (Def. 3.14). Once a path data scheme is created we lose the information about the exact routing of the original paths, but are able to realize operations to construct the same number of paths. These operations are summarized in a so-called path data structure (Def. 3.9). In particular, it allows us to determine the color of a vertex on a path or to run along the paths by finding the successor or predecessor of in time for some polynomial (Lemma 3.12). The idea here is to explore the region containing and use Lemma 3.1 to get a fixed set of paths connecting the equally colored vertices. By Lemma 3.4, the new paths still have our good properties. However, assuming that we have a set of good paths and some new chordless path , whose computation is shown in Lemma 3.19, is not necessarily good. Our approach to make the paths good is to find a rerouting that outputs a set of good paths where (Lemma 3.21). Let be the vertices of and . We realize by a mapping that defines the successor of a vertex as a vertex of another path with color . Intuitively speaking, we cut the paths in in pieces and reconnect them as defined in .
The rerouting may consist of successor information, each of bits. Therefore, we compute a rerouting by considering only the --subpath of for some vertex . We compute the rerouting in batches by moving closer to with each batch. Each batch consists of successor information (or less for the last batch). Using the rerouting and path data structures storing and , we realize a so-called weak path data structure for the paths We call it a weak path data structure because it can not be used to determine the color of a vertex. After that we use it to compute a new valid path data scheme for the paths such that we need neither the rerouting nor anymore (Lemma 3.23). However, since is only a subpath of we repeat the whole process described in this paragraph with another subpath of , i.e., the subpath of whose first vertex is adjacent to the last vertex of . Finally, we so get a valid path data structure storing good - paths (Corollary 3.25).
We now describe our approach in detail. To store a single --path we number the internal vertices along the path with etc. and store the numbers of all vertices inside an array of bits. For a vertex outside the paths, we set . We refer to this technique as path numbering. By the numbering, we know the direction from to even if we hit a vertex in the middle of the path. Note that completely defines .
To follow a path stored in , begin at a vertex and look for a neighbor of with . Note that this approach only works if has only one such neighbor. To avoid ambiguities when following a path we require that the path is chordless.
Lemma 3.6**.**
Assume that we are given access to a DFS that uses a stack to store a path from to the current vertex and that runs in time using bits. Then, there is an algorithm to compute a path numbering for a chordless --path in time by using bits.
Proof 3.7**.**
We call the internal subpath from a vertex to a vertex skippable exactly if consists of at least vertices and there exists a chord, i.e., an edge from to . The idea is to construct first an --path in with the DFS and, directly after finding , we stop the DFS and then pop the vertices from the DFS stack and remove skippable subpaths until we arrive at .
Let be the vertices on the DFS stack after reaching and let be an -bit array marking all vertices with a one that are currently on the stack. (We say that a vertex is marked in exactly if .) The algorithm moves backwards from to , i.e., by first popping , setting and then popping the remaining vertices from the DFS stack as follows.
Pop the next vertex from the stack, unmark in , and let be the vertex that is now on top of the DFS stack. We check ’s neighborhood in for all vertices that are marked in , mark them in an -bit array as chords and remember their number in . The internal subpaths between and all vertices marked in are skippable. We remove the skippable paths as follows: As long as , we pop a vertex from the DFS stack. If is marked in , we unmark in and reduce by one. If , we unmark in . When , the edge is the last chord, we set and and continue our algorithm as described in this paragraph until .
After removing skippable paths from , we can output the path as follows. Allocate an array with bits for each vertex and initialize all entries with [math]. Let . Starting from go to its only neighbor that is marked in . Set and . Unmark in , set , and continue assigning the path numbering until . Finally return .
Observe that the DFS runs once to find a path from to and then we only pop the vertices from the stack. During the execution of the DFS we manage membership of vertices using only arrays of bits. The construction of uses a single run over the path by exploring the neighborhood of each marked vertex. In total our algorithm runs in time and uses bits in addition to the time and space needed by the given DFS.
Recall that the path numbering does not uniquely define chordless vertex-disjoint paths since there can be edges, called cross edges, between vertices of two different paths in . Due to the cross edges, the next vertex of a path can be ambiguous (see again Fig. 1). Coloring these vertices would solve the problem, but we may have to many to store their coloring.
Our idea is to select a set of vertices that we call boundary vertices such that holds and by removing from we get a graph that consists of connected components of size . In particular, contains all vertices with large degree, i.e., vertices with a degree strictly greater than . Note that a graph with treewidth has at most edges, and so we have only vertices of large degree. We store the color of all boundary vertices . This allows us to answer their color directly. A region is a connected component in . Note that a boundary vertex can connect several regions. To avoid exploring several regions when searching the predecessor/successor on the same path of a vertex , we additionally store the color of the predecessor/successor of each . According to our described setting above, we formally define our scheme.
Definition 3.8**.**
(Path Data Scheme) A path data scheme for in is a triple where
- •
* is an array storing the path numbering of all paths in ,*
- •
* is a set of all boundary vertices with and defines regions of size , and*
- •
* stores the color of every vertex in and of each of their predecessors and successors.*
We realize as well as as arrays of bits, and as a -bit data structure using static space allocation. Altogether our path data scheme uses bits.
A further crucial part of our approach is to (re-)compute paths fast (in particular, we do not want to use a -disjoint-path algorithm), but we also want to guarantee that vertices of the same color get connected with a path constructed by a deterministic network-flow algorithm. In other words, our path data scheme must store the same color for each pair of colored vertices that are connected by our fixed algorithm of Lemma 3.1. We call a path data scheme that has this property valid (with respect to our fixed algorithm).
To motivate the stored information of our path data scheme, we first show how it can be used to realize a path data structure that allows us to answer queries on all vertices and not only a fraction of . Afterwards, we show the computation of a path data scheme.
Definition 3.9**.**
(Path Data Structure) A path data structure supports the following operations where .
- •
: Returns the predecessor and the successor, respectively, of .
- •
: Returns the color of a path to which belongs to.
To implement the path data structure, our idea is first to explore the region in containing the given vertex by using a BFS and second to construct a graph of the vertices and edges visited by the BFS. We partition the visited colored vertices inside the region into two sets (successors of ) and (predecessors of ), as well as extend the graph by two vertices and that are connected to and , respectively. Then we sort the vertices and its adjacency arrays by vertex ids and run the deterministic network-flow algorithm of Lemma 3.1 to construct always the same fixed set of paths (but not necessarily the original paths). The construction of the paths in a region consisting of a set of vertices is summarized in the next lemma, which we use subsequently to support the operations of our path data structure. We use the next lemma also to make a path data scheme valid. We so guarantee that our network-flow algorithm connects equally colored vertices and no -disjoint path algorithm is necessary. This is the reason why the following lemma is stated in a generalized manner and does not simply assume that a path data scheme is given.
Lemma 3.10**.**
Assume that we are given the vertices of a region in as well as two sets of all successors and predecessors vertices of vertices on the boundary, respectively. Take . Then time and bits suffice to compute paths connecting each vertex in with another vertex in .
Proof 3.11**.**
We construct a graph , add two new vertices and and connect them with the vertices of and , respectively. To structurally get the same graph independent of the permutation of the vertices in the representation of the set we sort the vertices and the adjacency arrays of the graph representation for . The details to do that are described in the three subsequent paragraphs. Finally we run the algorithm of Lemma 3.1 to compute all --paths in the constructed graph . Since and are the endpoints of disjoints subpaths of paths in , we can indeed connect each vertex in with another vertex in .
We now show the construction of . We choose an arbitrary and run a BFS in graph starting at three times. (We use a standard BFS with the restriction that it ignores vertices of that are not in .)
In the first run we count the number of explored vertices. Knowing the exact number of vertices and knowing that all explored vertices can have a degree at most in , we allocate an array of fields and, for each (), an array of fields, each of bits. We will use to store the adjacency arrays for isomorphic to . For reasons of performance, we want to use indirect addressing when operating on . Since the space requirement to realize indirect addressing depends on the largest value in , we use a bidirectional mapping from to and use vertex names out of for . More exactly, to translate the vertex names of to the vertex names of , we use a translation table , and we realize the reverse direction by using binary search in , which can be done in time per access. For the table we allocate an array of fields, each of bits.
In a second run of the BFS we fill with the vertices explored by the BFS and sort by the vertex names. In a third run, for each vertex explored by the BFS, we determine the neighbors of sorted by their ids and store in . During the third BFS run we want to store also two sets and (using a standard balanced tree representation). Now, allows constant time accesses to a graph isomorphic to . Afterwards, we are able to compute the paths in and using the mapping translate the vertex ids in back to vertex ids in .
Efficiency: We now analyze the space consumption and the runtime of our algorithm. Since has treewidth and consists of vertices of a region of , we can follow that has vertices and edges. uses bits to store pointers, each of bits, that point at adjacency arrays. The adjacency arrays use bits to store vertex ids out of . The translation table and the sets and use bits. The BFS can use bits. The space requirement of an in-place sorting algorithm is negligible. Lemma 3.1 runs with bits. In total our algorithm uses bits.
To compute the graph and fill we run a BFS. Running the BFS comes with an extra time factor of to translate a vertex ids of to a vertex ids of (i.e., to access values in ) and costs us time in total. Since has edges has non-zero entries and a sorting of can be done in time. Sorting can be done in the same time. Finally, Lemma 3.1 has to be executed once for up to paths (every vertex of the region can be part of ), which can be done in time since bounds the edges of the region and since we get an extra factor of by using the translation table . In total our algorithms runs in time .
Since the number of vertices in a region is bound by , we can use Lemma 3.10 and the coloring to support the operations of the path data structure in the bounds mentioned below.
Lemma 3.12**.**
Given a valid path data scheme we can realize and in time as well as in time. All operations use bits.
Proof 3.13**.**
In the case that a vertex is in , we find its color and the color of its predecessor and successor in . (Vertices neighbored to or have as the predecessor and as the successor, respectively.) Thus, we can answer and by iterating over ’s neighborhood and determining the two neighbors that are colored the same as . By using the numbering we know the incoming and outgoing edge of the path through and so know which of the vertices and is the predecessor or successor of .
For a vertex , we explore ’s region in by running a BFS in with the restriction that we do not visit vertices in . We use a balanced heap to store the set of visited vertices. Moreover, we partition all colored vertices of into the set (of successors of ) and the set (of predecessors of ) with respect to the information in . In detail, if a colored vertex has an equally colored neighbor , then is the successor of if holds and the predecessor of if holds.
Having , and we call Lemma 3.10 to get the paths in the region. Note that a path within a region can be disconnected by vertices with large degree so that we can have more than two vertices of each color in and . E.g., a path may visit with and . Since our path data scheme is valid, we can conclude the following: assume that we connect the computed subpaths using their common equally colored boundary vertices. By Lemma 3.2 each solution has to use all vertices and the network-flow algorithm indeed connects with , with , etc. By Lemma 3.4 the paths and thus our computed subpaths are chordless and have no extended deadlock cycles in with respect to . Using the paths we can move along the path of to a colored vertex and so answer ’s color and both of ’s neighbors on the path.
Efficiency: We now analyze the runtime of our algorithm and its space consumption. Realizing the operations and for a vertex of can be done in time since both colored neighbors have to be found, and their color can be accessed in constant time using . The operation runs in constant time. For the remaining vertices we have to explore the region in . Since every region has vertices and also treewith , the exploration can be done in linear time per region, i.e., in time using a standard BFS. Filling , and requires us to add vertices into balanced heaps, which uses time. The execution of Lemma 3.10 can be done in time and running along the paths to find the actual color runs in time. Thus, in total and run in time and in time .
A BFS to explore a region in uses bits, while , and use bits in total. The execution of Lemma 3.10 uses bits, which is also an upper bound for all remaining operations.
To be able to compute a storage scheme for paths, must satisfy certain so-called good properties that we summarize in Definition 3.14.
Definition 3.14**.**
(good paths) We call a set of --paths good if and only if
all paths in are pairwise internal vertex disjoint, 2. 2.
each path in is chordless, and 3. 3.
there is no extended deadlock cycle in with respect to .
The next lemma shows the computation of a valid path data scheme for one path . The computation is straightforward. Since there is only one path, this path is uniquely defined by the path numbering in and we can run along it and select every vertex as a boundary vertex and compute and store the remaining information required for a valid path data scheme. After the lemma we always assume that a chordless path is given together with a valid path data scheme and thus a path data structure so that we can easily access the predecessor and successor of a vertex on the path.
Lemma 3.15**.**
Given a path numbering for one chordless - path , we can computes a valid path data scheme for in time using bits.
Proof 3.16**.**
Since stores a single chordless path , we can run along it from to while adding every th vertex and every vertex of large degree into an initially empty set . We then can easily determine the predecessor and successor. Thus, we can store using static space allocation. A path data scheme storing a single path is valid by default since there is no second path to that an algorithm could switch. We so get a path storage scheme storing good --path in time using bits.
Assume that, for some , we have already computed good --paths, which are stored in a valid path data scheme. Our approach to compute an th --path is based on the well-known network-flow technique [1] described in the beginning of the section, which we now modify to make it space efficient.
By the standard network-flow technique, we do not search for vertex-disjoint paths in . Instead, we modify into a directed graph and search for edge-disjoint paths in . To make the approach space efficient, we do not store separately, but we provide a graph interface that realizes .
Lemma 3.17**.**
Given an -vertex -edge graph there is a graph interface representing an directed -vertex -edge graph where and and thus and . The graph interface allows us to access outgoing edges and incoming edges of , respectively, by supporting the operations , and , . The graph interface can be initialized in constant time and the operations have an overhead of constant time and bits.
Proof 3.18**.**
*We now show how to compute the operations for from on the fly. For each vertex in , we define two vertices and for . By the transformation we can see that every vertex (with ) has exactly one outgoing edge to and incoming edges from some vertices (). Moreover, every vertex (with ) has outgoing edges to some vertices () and one incoming edge from . With these information we can provide our stated operations for .
[TABLE]
[TABLE]
The operations , and , have the same asymptotic bounds as and , respectively.
We next show that we can compute an st paths.
Lemma 3.19**.**
Given a valid path data scheme for a set of good --paths in , time and bits suffice to compute an array of bits storing a path numbering of an th chordless --path , which can be a dirty path with respect to .
Proof 3.20**.**
To solve the lemma, we adapt the standard network-flow approach as follows. We first use the graph interface of the Lemma 3.17 to obtain a graph in that we can search for edge-disjoint paths instead of vertex-disjoint paths in . We provide the operations , , and a virtual array that gives access to the paths adjusted to by using a simple translation to the corresponding data structure of .
According to the construction of a vertex in is equivalent to two vertices, an in-vertex and an out-vertex in . Moreover, every in-vertex in has a single outgoing edge and this edge points to its out-vertex , which has only outgoing edges that all point to some in-vertices in . Taken our stored good paths into account a path in translates into a path in and the edges between them must be reversed in .
Next, we want to build the residual network of and . This means that we have to reverse the edges on the paths of . To identify the edges incident to a vertex , it seems to be natural to use and . Unfortunately, we cannot effort to query these two operations many times to find out which of the incident edges of is reversed since can be a large vertex and the runtime of both operations depends on . This issue becomes especially important when using a space-efficient DFS that may query more than a constant number of times (e.g., by running several restorations of a stack segment including ).
To avoid querying for any vertex of , we present the DFS a graph where has as outgoing edges first all outgoing edges of in and then all incoming edges of in . In detail, we present the DFS a graph with all vertices of and one further vertex with no outgoing edges. We also make sure that the DFS has colored black (e.g., start the DFS on before doing anything else). A sketch of the graph with a blue path, graph and the reversal of the edges in can be seen in Fig. 4. As we see in the next paragraph, we will use as a kind of dead end.
Every vertex of has many outgoing edges defined as follows. The heads of the first outgoing edges of in are the same as in with the exception that we present edge for possibly one outgoing edge that is reversed (i.e., ). The heads of the next incoming edges of in are presented as an edge with the exception that we present possibly one incoming edge that is reversed as an outgoing edge .
Intuitively speaking, this ensures that a DFS backtracks immediately after using edges that do not exist in the residual graph. Note that we so can decide the head of an edge in by only calling twice. The graph has asymptotically the same amount of vertices and edges as . To guarantee our space bound we compute a new --path in using a space-efficient DFS on and consecutively make chordless by Lemma 3.6. Finally, we transform into a path with respect to by merging in and out vertices together.
Efficiency: Concerning the running time we use a space-efficient DFS (Lemma 2.6) to find a new --path in . To operate in we need to query the color of vertices. We pay for that with an extra factor of (Lemma 3.12) in the running time. Since our graph has vertices and edges we find a new - path in in time . The time to transform the found path into and store it can be done in the same bound. The total space used for the algorithm is bits— bits for the space-efficient DFS, bits for Lemma 3.12, and bits for storing and its transformation.
Let us call an --path a clean path with respect to if is a set of good paths, and a dirty path with respect to if for all , the common vertices and edges of and build subpaths each consisting of at least two vertices and used by the two paths in opposite direction when running from to over both paths. Intuitively, if we construct an st path in the residual network of with paths in , then can run backwards on paths in , and we call a dirty path (see Fig. 5). After a rerouting, we obtain internal vertex disjoint - paths. To store the paths with our path data structure, the paths must be good, which we can guarantee through another rerouting. The details are described below.
Let be an --path returned by Lemma 3.19. We first consider the case where is a dirty path with respect to . To make the paths good we cut the paths into subpaths and by using the subpaths we construct a set of new good --paths. To achieve that we have to get rid of common vertices and extended deadlock cycles. We handle common vertices and extended deadlock cycles in a single process, but we first briefly sketch the standard network-flow technique to remove common vertices of with a path . By the construction of the paths, the common vertices of an induced subpath are ordered on as (for some function and ) and on as . and can be split into vertex disjoint paths by (1) removing the vertices from as well as from , (2) by rerouting path at to follow , and (3) by rerouting at to follow .
We denote by a rerouting function that returns a new set of good paths. We so change the successor and predecessor information of some vertices of the paths . Let be a set of all vertices in . To define successor and predecessor information for the vertices we use the mappings where for each vertex with , ’s new successor is a vertex with . Similarly we use to define a new predecessor of some . The triple realizes a rerouting .
To avoid using too much space by storing rerouting information for too many vertices our approach is to compute a rerouting only for an --subpath of where is a vertex of . This means that consists of --paths and one --path. Moreover, we make the rerouting in such a way that, for all paths , there is a vertex with the following property: replacing the --subpath of by an edge for all paths we get good paths. Let consist of the --subpaths for each . We then call the set of vertices part of the paths in a clean area for .
The idea is to repeatedly compute a rerouting in batches and thereby extend with each batch by vertices—the last batch may be smaller—such that we store entries in and with each batch. After each batch we free space for the next one by computing a valid path storage scheme storing good - paths , where is the only path in not ending in , and a valid path storage scheme for the - path obtained from by replacing trough . (For an example consider the --subpath of in Fig. 6a, which is replaced by a beginning of in Fig. 6d.)
We now sketch our ideas of the rerouting that removes extended deadlock cycles in . Recall that every extended deadlock cycle must contain parts of since all remaining paths are good. Hence by moving along we look for an vertex of that is an endpoint of a cross edge . Since a deadlock cycle is a cycle, there must be some vertex after on that is connected by subpaths of and cross edges connecting the paths in .
Since common vertices and deadlock cycles may intersect, vertices and can have a cross edge or be common vertex on a path in (Fig. 6a). To find and , our idea is to move over starting from and stop at the first such vertex of . Then use a modified DFS at that runs over paths in only in reverse direction and over cross edges—but never over two subsequent cross edges—and so explores subpaths of (marked orange in Fig. 6b). Whenever a vertex of the clean area is reached, the DFS backtracks, i.e., the DFS assumes that has no outgoing edges. We so guarantee that the clean area is not explored again and again. Afterwards we determine the latest vertex on that is a common vertex or has a cross edge with one of the explored subpaths. If such a vertex is found, we either have a common subpath from to between a path and (which is removed as described above) or a deadlock cycle. If it is an extended deadlock cycle, we have a subpath on the cycle consisting of at least three vertices. As the proof of the following lemma shows the extended deadlock cycle can be destroyed by removing the inner vertices of the subpath and rerouting the paths via cross edges part of the extended deadlock cycle. We find an extended deadlock cycle by an additional run of the modified DFS from to (Fig. 6c) and reroute it along the cross edges. Fig. 6d shows a rerouting where the path in starting with the vertices of the old path becomes the “new” path .
An --path is called a clean subpath with respect to a set of good paths if, for the extension of by an edge and a vertex , is a set of good paths. The green path in Fig. 5 is a clean subpath. The next lemma summarizes the computation of our rerouting . We initially call the lemma with a clean subarea and a clean path that consists only of vertex .
Lemma 3.21**.**
There is an algorithm that accepts as input a valid path data scheme for good --paths , a valid path data scheme for a possibly dirty - path , as well as a clean area for including a clean - subpath of and outputs a rerouting , a clean area including a clean - subpath with the properties that is a subpath of , and . The algorithm runs in time and uses bits.
Proof 3.22**.**
We begin to describe our algorithm to detect common vertices and extended deadlock cycles. Afterwards we compute the rerouting which is realized by . Initially, is an array of bits consisting of all vertices of . During the algorithm we remove vertices from whenever we change our paths. We want to use static space allocation to store the mappings and , which requires us to know the key set of the mapping in advance. We solve it by running the algorithm twice and compute the key set in the first run, reset and from a backup, and compute the values and store them in a second run. For simplicity, we omit these details below and assume that we can simply store the values inside and .
Starting from vertex we run along path . We stop at the first vertex if is a common vertex of and a path in (Fig. 6a) or it is an endpoint of a cross edge. For a simpler notion, we call such a vertex of a vertex touching the vertices in . Next we use a DFS without restorations (due to our details described below, the DFS stack consists of only vertices, which are incident to cross edges of the current DFS path). We so explore the vertices from that are reachable from via edges on the paths used in reverse direction and cross edges, but ignore a cross edge if it immediately follows after another cross edge and ignore the vertices in the clean area . To allow an economical way of storing the stack, the DFS prefers reverse edges (edges on a path ) compared to cross edges when iterating over the outgoing edges of a vertex. This guarantees that the vertices on the DFS-stack consist of at most subpaths of paths in . We store the vertices processed by the DFS in a choice dictionary [22, 24, 29] since a choice dictionary allows us to compute in a time linear to the amount of elements in . Moreover, we count the number of vertices of that touch . Running over starting from we can determine the last such vertex (Fig. 6b).
Then we run a modified DFS to construct a - path that (1) consists only of vertices in , that (2) uses no subsequent cross edges and that (3) uses every path in only once (Fig. 6c). Note that these restrictions (1) – (3) still allow us to reach all vertices of . To ensure restriction (3) we maintain a bit array of forbidden paths where exactly if a subpath of is part of the currently constructed path. To construct the DFS starts at vertex and processes a vertex as follows:
Stop the DFS if is reached. 2. 2.
*// To guarantee restriction (1):
*If , then backtrack the DFS and color white. 3. 3.
Iterate over all reversed edges : recursively process . 4. 4.
*// To guarantee restriction (2) and (3):
**Iterate over all cross edges : *
If and was not discovered by a cross edge, recursively process . 5. 5.
Backtrack the DFS to the predecessor of on . If is a cross-edge, color white.
// Coloring white guarantees that we can explore outgoing cross-edges of if we reach again over a reverse edges.**
Let be the colors of the paths in and be the color of . In the case that and are both on one path of , we reroute the paths as follows. Set , and update accordingly, i.e., remove all internal vertices of the - subpath of from (Fig. 7a). Afterwards, we set and and repeat the whole algorithm above.
Recall that the construction of the - path uses only one subpath of every path in . Without loss of generality, let be , where are cross edges and are subpaths of a path (for some order of and some with ).
We first assume that and are both cross edges (Fig. 7b). If the --subpath of every as well as the --subpath of consists of exactly two vertices, then we found a simple deadlock cycle, in particular, and are the only vertices of that touch . We keep the simple deadlock cycle by setting and and repeat the whole algorithm above.
Otherwise we have an extended deadlock cycle and we compute a rerouting as follows. For every cross edge with on the DFS stack, we set , , remove all vertices of each path in between and as well as all vertices of path in between and from . We now describe the rerouting at . Vertex is handled analogously. If as assumed is a cross edge, set and (Fig. 7c). Otherwise, is on path before vertex , or . Then remove all vertices in between and on path from and set and (Fig. 7d). For the case where neither nor is a cross edge, as well as and are on different paths, see Fig. 7e for the rerouting. After all cases described in this paragraph set , and repeat the whole algorithm above.
Note that in all cases above, we only add information for predecessors and successors with each extension of to our rerouting. Thus, our rerouting information increases in small pieces. Whenever is defined for vertices ( uses bits) or we reached while moving over , we break the algorithm above. By applying Lemma 3.6 on each rerouted path (the graph induced by the vertices of the paths is taken as input graph), we can make all paths chordless without introducing further cross edges or extended deadlock cycles. Finally, return the rerouting as well as the new clean area and the end vertex of a new clean path in .
Correctness. One can see that our algorithm discovers every common subpath of with a path as well as every extended deadlock cycle since, in both cases, we have a vertex followed by on such that is discovered from while running backwards on . Since only shrinks (no new cross edges can occur), since by construction the new paths have no common vertices accept and (here it is important that the constructed - path has no subsequent cross edges) and since we choose as the latest vertex of touching , maintains a clean area when adding to it. Note that each vertex with a rerouting information becomes part of . Thus, the size of increases by when our algorithm stops unless we reach while running on .
Efficiency. Concerning the running time, note that we travel along as well as construct a set with a DFS on the paths in . Note that we process all vertices of at most once by each of the two standard DFS runs (once to construct as well as once to find a - path for computing the rerouting) before is added into . Within the processing of each vertex , we call only once. Since each access to a vertex can be realized in time (Lemma 3.12), the total running time is .
Concerning the space consumption observe that and , a backup of and as well as all arrays use bits. To run the DFS, bits suffice (storing the colors white, grey and black in bits and bits for the DFS stack since we only have to store the endpoints of subpaths that are part of the stack). Together with the space used by Lemma 3.12, our algorithm can run with bits.
We can not store our rerouted paths directly in a valid path data scheme. To follow the path with respect to the rerouting we next provide a so-called weak path data structure for all - paths of .
Recall that we have a path data scheme storing the single path and a path data scheme storing the set of paths. Let , as well as (realized by ) be returned by the previous lemma. Our valid path data scheme can only store --paths, but may contain an - path with . In the case that , we compute a path numbering and a valid path data scheme for the path obtained from by replacing the subpath by . (The path replaces the path in the computation of the next batch.) Furthermore, we remove the path from by removing its vertices from . It is easy to see that this modification of the paths can be done in the same bounds as stated in Lemma 3.21.
After the modification of the paths we show how to follow the paths with respect to . Since we can find out in time if a vertex belongs to , a call of and below is forwarded to the correct path data scheme. We now overload the and operations of our path data structure as follows, and so get a weak path data structure for that supports and in time by using bits and that supports access to in constant time. To obtain the runtime we realize and by computing the paths within at most one region, from which we know the color of all neighbors of . In detail, the weak path data structure supports the following operations where .
- •
: If , return . Else, return with .
- •
: If , return . Else, return with .
- •
\mathtt{inVStar}$$(u): Returns true exactly if .
To compute a valid path data scheme for good --paths we have to partition into regions. More exactly we have to find a set of boundary vertices such that consists of regions (i.e., connected components) of size . To find the boundary we start with the set of neighbors of that belong to and move to the right “towards” such that always is a separator that disconnects and in . Whenever there are vertices “behind” the previous separator that was added to our boundary, we add to our boundary—see also Fig. 8. While moving towards , we always make sure that the endpoints of a cross edge are not in different components in . This means, we do not move over an endpoint of a cross edge unless the other endpoint is also in . We show in the next lemma that this is possible if we have no extended deadlock cycles and if we use the following trick for simple deadlock cycles: move one vertex further along the path for all vertices part of the simple deadlock cycle.
In the case that the previous lemma returned paths containing an - path with , then our weak path data structure gives access to good - paths. Otherwise, and we have access to good - paths.
Lemma 3.23**.**
Given a weak path data structure for accessing good - paths there is an algorithm that computes a valid path data scheme storing good - paths. The algorithm runs in time and uses bits.
Proof 3.24**.**
Let be the vertices of and obtained from the weak path data structure. As in the last proof, we assume that the paths in are colored from . For the case that , we compute a new path data scheme for by Lemma 3.15. Moreover, we remove the vertices of from and start to compute our path data scheme for . Our algorithm works in three steps. First we compute the boundary, then a path data scheme, and finally make it valid.
Compute the boundary. Start with the set of all neighbors of in . Note that the vertices in are candidates for a boundary because has the property that it cuts all --paths; in other words, there is no path in that connects and . For performance reasons, we cache for each vertex in as long as is in so that we have to compute it only once. From we can compute the next set of candidates for a boundary. The idea is to try to put for each vertex the vertex of the same path into . However, we cannot simply do this if has a cross edge that can bypass . In that case we take into . If , then one (or multiple) deadlock cycles restrain us to add new vertices to . We are either able to take the next vertex of every vertex of that is part of a a simple deadlock cycle into or show that an extended deadlock cycle exists, which is a contradiction to our property that our paths are good.
We now focus on the structures that we use in the subsequent algorithm. Let (initially ) be a set consisting of all vertices of the connected component of that contains as well as the vertices of . (Intuitively speaking, is the set of vertices for which the membership in a boundary was already decided.) We associate the index of a vertex with its path membership (not its color since a weak path data scheme does not provide color information). Technically we realize and as arrays of fields such that and store vertices and , respectively, of (). Moreover, for each on path we store the number in an array of fields. These numbers represents the number of edges whose head does point at a vertex outside of . If , then has a cross edge to some vertex not in and we cannot simply put inside , because we might get a cross edge that can be used by an - path in . Otherwise, if , then has no cross edge possibly bypassing and we can take into . Let be an initially empty set of boundary vertices. To know when the next set can be added to the boundary such that the property that the boundary defines regions of size holds, we use a counter (initially ) to count the number of vertices that we have seen until extending the boundary the last time.
We now show our algorithm to compute given . Take , initialize as for each , and compute a set in phases as follows. For each , if , take into , increment by one, and set . Moreover, for each decrement by one. If , take into . If , add into , set and repeat the phase.
Otherwise (i.e., ) we run into the special case where we have encountered a deadlock cycle (possible a combination of deadlock cycles), which we can handle as follows. Since is good and is clean, we have one or more simple deadlock cycles with vertices in . Set and take . Note that all these simple deadlock cycles have only vertices in , but not all vertices of have to be part of a simple deadlock cycle. To compute from , we have to remove the vertices from that are not part of the simple deadlock cycle, which we can do as follows. Take . Check in rounds if separates from , i.e., for each with , choose and check if . If not, additionally check if a exists. If one of the check passes, some edge of bypasses and we replace by in and decrement by one. Then repeat the round until no such can be found for any vertex. Afterwards, set as the new , add into , and compute the -values for the new vertices in , and proceed with the next phase.
If after adding into , add the vertices of to , and reset . Repeat the algorithm until and add the vertices of to . Finally, add all vertices of with large degree to .
Correctness of the boundary. We show the following invariant whenever we update : the removal of in disconnects and . Initially, this is true for the neighbors of because their removal disconnects each chordless path in . If we have a vertex on with only one neighbor in , i.e., we have , then is again a valid separator. If we have no such vertex , then we construct a set and we check for each vertex in if it has a cross edge that destroys being a separator. Thus, we set only if is again a separator.
We next show that we make progress with each separator over all phases, i.e., we find a new separator so that increases. For a simpler notation, let us say that a vertex on a path is behind another vertex on the path if the latter is closer to . As long as we do not run into the special case having a deadlock cycle, we can clearly make progress. Let be defined as in the beginning of the special case, i.e., consists of the successors on the paths of each vertex in . Let us consider a deadlock cycle whose subpaths all begin with a vertex in . Let be the endpoints of the subpaths. Choose such that no cross edge of a vertex ends at a vertex on a path that is part of another deadlock cycle such that is behind the subpath part of .
Assume for a contradiction that no such exists. Then we must have a cyclic sequence of deadlock cycles where each deadlock cycle has a vertex in with a cross edge to a vertex of the next deadlock cycle (Fig. 9a shows an example for ). But then we can use the cross edges to build an extended deadlock cycle. (Fig. 9b).
For a path , let us consider the successor on of a vertex in that is replaced first among all vertices in in the current phase. Clearly, it is replaced by in . Then we have a cross edge from either to (Case 1) a path that does not begin with a vertex in or (Case 2) ends behind a vertex of . In the Case 1, by the property of , can not be part of a deadlock cycle and we are not in the special case. In the Case 2, by the property of , must end on a vertex that belongs to the deadlock cycle. Since ends behind a vertex of , we have an extended deadlock cycle; a contradiction. Thus, the vertices being successors of remain in and we make progress in the special case.
Computing the new valid path data scheme. We know and the new boundary . For a valid path data scheme it remains to compute a path numbering , the colors for the boundary vertices and their predecessors and successors that we store in an -bit structure using static space allocation. We can compute by moving along each path from to .
It is important that all vertices of an - path are colored with the same color, however we want to use Lemma 3.10 that computes path inside one region. Since - paths are disconnected by boundary vertices our approach is to start with an arbitrary coloring of the neighbors of part of the paths that are the first boundary vertices and from these make a parallel run over the paths. Whenever we enter a region we explore it and compute paths within the region. Using the paths we propagate the colors of the boundary vertices along the paths to the next boundary vertices, their predecessors and successors and repeat our algorithm.
Let (initially ) be the set of vertices for that we have already decided their color. To find a fixed routing within a region we explore the region of every neighbor of in using a BFS that skips over vertices in . We collect all visited vertices of one region in a set realized by a balanced heap. (If is empty, the region was already explored from another neighbor of a vertex of and we continue with the next vertex .) In addition we construct two sets and . Each visited vertex is put in if holds and put in if holds (a vertex can be part of as well as if both holds). We apply Lemma 3.10 with , and as input and get paths connecting each vertex of with another vertex of . We set to avoid the exploration of explored regions again.
Let be a set of vertices whose successors of are in . Intuitively, by having we can avoid computing the paths of a region again and gain. For each vertex we use and so move to a vertex of —we are now on a path computed by Lemma 3.10. We store the color of for this vertex and run along the path until it ends. We store the color for the last vertex of the path and use to move to a boundary vertex that we also color with . If the vertex is on a computed path we repeat this paragraph with . Otherwise, we put into a set and remove from . If loses its last vertex we set and and repeat the iteration. Otherwise, we proceed with the next vertex .
While moving over the paths we extend by the visited vertices. Moreover we compute the path numbering for and all colors are stored in . Recall that in order to use static space allocation we first need to know the set of all vertices in advance that we afterwards want to color. Do get that set we can run the algorithm in two steps. In the first we do not store the colors but only compute a set of vertices that must be colored. Using it as a key set for static space allocation we repeat our algorithm in a second step and store the coloring. The triple represents the new valid path data structure.
Correctness of the new valid path data scheme. The boundary and the rerouted paths represented by the weak path data structure fixes the boundary vertices and their predecessor and successors. Hence our selection of the vertices for , and is also fixed for each region. Computing the paths using Lemma 3.10 fixes the path numbering. Since a (re)computation with the same algorithm produces the same paths (being subpaths of our - paths) and since we used the paths to propagate the coloring, the endpoints of a path and thus, all all colored vertices along a path are equally colored in . Therefore our path data scheme is valid. Moreover, and consists of the endpoints of subpaths of good paths. Hence, a network-flow algorithm can connect each vertex of with another vertex of and by Lemma 3.4 our paths are chordless and deadlock free paths, it also holds for the paths of them.
Efficiency. We first consider the time to compute the boundary. For each new vertex in , we spend time to determine once its degree in and to decrement already stored degrees of the neighbors of . In each phase we iterate over all elements of and make progress on at least one path, which means that we need at most phases. Determining the previous / next vertex on a path can be done in time . Dealing with simple deadlock cycles requires us to compute the initial members of , which cost us time and roundwise replace members in by their predecessors. Because , this can cause rounds. We spend time to check if is a separator. Thus, time allows us to compute a separator in the special case, and time allows us to add the at most vertices to within each phase.
To construct a valid path data scheme we need to explore all regions once, which can be done in time linear to the size of each region plus the number of edges that leave the region, i.e., in time. The construction of and requires the execution of and on each vertex once, which runs in time. Lemma 3.10 uses time for a region of size . Using it on every region once can be done in time. Propagating the color requires us to move along the paths, which can be done in in time time. The total time of the algorithm is .
Our space bound is mainly determined by , , , and each of bits and the application of Lemma 3.12 ( bits), which is used in the weak path data structure whenever we access a previous or next vertex on a path. For , and (and their copies) we need bits. The construction of the paths inside regions uses bits (Lemma 3.10). In total, we use bits.
Given path storage scheme storing good --paths and an th path we can batchwise compute a path storage scheme storing good - paths by subsequently executing times Lemma 3.21 and Lemma 3.23.
Corollary 3.25**.**
Given a path storage scheme storing good --paths and an th dirty path with respect to we can compute a path storage scheme storing good - paths in time using bits.
Initially our set of --paths is empty. Then a repeated execution of Lemma 3.19 and Corollary 3.25 allows us to show our final theorem for computing vertex-disjoint --paths in time.
Theorem 3.26**.**
Given an -vertex graph with treewidth and two vertices there is an algorithm that can compute a maximum amount of chordless vertex-disjoint paths from to in time using bits.
Knowing a maximum set of vertex-disjoint paths between two vertices and , we can easily construct a vertex separator for and .
Corollary 3.27**.**
Given an -vertex graph with treewidth , and two vertices and , time and bits suffice to construct a bit array marking all vertices of a vertex separator for and .
Proof 3.28**.**
First construct the maximum number of possible chordless pairwise vertex-disjoint paths from to (Theorem 3.26). Then try to construct a further - path with a DFS as describe in the proof of Lemma 3.19 and store the set of vertices that are processed by the DFS. Finally run along the paths from to and compute the set consisting of the last vertex on each path that is part of . These vertices are an --separator.
Many practical applications that use treewidth algorithms have graphs with treewidth for an arbitrary , and then our space consumption is bits.
4 Sketch of Reed’s Algorithm
In this section we first sketch Reed’s algorithm to compute a tree decomposition and then the computation of a so-called balanced -separator. In the following sections, we modify his algorithm to make it space efficient.
Reed’s algorithm [36] takes an undirected -vertex -edge graph with treewidth and an initially empty vertex set as input and outputs a balanced tree decomposition of width . To decompose the tree he makes use of separators. An -separator is a set such that separates among the connected components of and no component contains more than vertices of . A balanced -separator is an -separator with the additional property that no component of contains more than vertices.
The decomposition works as follows. If , we return a tree decomposition consisting of a tree with one node (the root node) and a mapping with . Otherwise, we search for a so-called balanced -separator of size that divides such that consists of connected components . Then, we create a new tree with a root node , a mapping , and set to . For each graph with , we proceed recursively with and . Every recursive call returns a tree decomposition (). We connect the root of to , we then set for all nodes . After processing all elements of return the tree decomposition .
Since a balanced -separator is used, the tree has a depth of , and thus the recursive algorithm produces at most stack frames on the call stack—each stack frame is associated with a node of . A standard implementation of the algorithm needs a new graph structure for each recursive call. In the worst-case, each of these graphs contains of the vertices of the previous graph. Thus, the graphs on the stack frame use bits. Storing the tree decomposition requires bits as well. The various other structures needed can be realized within the same space bound. In conclusion, a standard implementation of Reed’s algorithm requires bits.
The next lemma shows a space-efficient implementation for finding a balanced -separator.
Lemma 4.1**.**
*Given an -vertex graph with treewidth and of at most vertices, there is an algorithm for finding a balanced -separator of size in that, for some constant , runs in time and uses bits. *
Proof 4.2**.**
We now sketch Reed’s ideas to compute a balanced -separator. To compute a balanced -separator we compute first an -separator . To make it balanced, we compute an additional -separator where is a set of vertices that is in some sense equally distributed in . Then is a balanced -separator.
Reed computes an -separator by iterating over all possibilities to split into three vertex-disjoints sets , and with and . For each iteration connect two new vertices and with all vertices of and , respectively, compute vertex disjoint paths with Corollary 3.27 to find a separator and check if holds.
We now shortly describe Reed’s computation of the set . Run a DFS on the graph and compute in a bottom up process for each vertex of the resulting DFS tree the number of descendants of . Whenever this number exceeds , add to the initially empty set and reset the number of descendants of to zero. At the end of the DFS, the set consist of at most vertices, which can be used to compute a balanced -separator as described above. Similar to the -separator, we compute an -separator.
We now show how to compute using bits. To prove the lemma, we use the following observation. Whenever the number of descendants for a node is computed, the numbers of ’s children are not required anymore. The idea is to use a balanced parentheses representation, which consists of an open parenthesis for every node of a tree, followed by the balanced parentheses representation of the subtrees of every child of , and a matching closed parenthesis. Consequently, if is a vertex with descendants having its open parenthesis at position and its closed parenthesis at position , then the difference between and is . Note that, taking an array of bits, we can store the number of descendants of in as a so-called self-delimiting number by writing as . This means that we overwrite the self-delimiting numbers stored fo the descendants of the children of .
To construct we run a space-efficient DFS twice, first to construct a balanced parentheses representation of the DFS tree, which is used to compute the descendants of each vertex in the DFS tree and so choose vertices for the set , and a second time to translate the ids of the chosen vertices since the balanced parentheses representation is an ordinal tree, i.e., we lose our original vertex ids and the vertices get a numbering in the order the DFS visited the vertices. After choosing the vertices that belong to the set and marking them in a bit array , we run the DFS again and create a bit array that marks every vertex that the DFS visits as the th vertex if and only if is marked in .
It remains to show how to compute the bit array . Let be a bit array of bits storing the balanced parentheses representation, and let be a bit array of bits that we use to store the numbers of descendants for some vertices. Note that a leaf is identified by an open parenthesis followed by an immediately closed parenthesis. Moreover, since the balanced representation is computed via a DFS in pre-order, we will visit the vertices by running through in the same order. Note that Munro and Raman [35] showed a succinct data structure for balanced representation that initializes in time and allows to compute the position of a matching parenthesis, i.e., given an index of an open (closed) parenthesis there is an operation () that returns the position of the matching closed (open) parenthesis.
The algorithm starts in Case with (). We associate [math] with an open parenthesis and with a closed parenthesis.
Case 1
Iterate over until a leaf is found at position , i.e., find an with . Since we found a leaf we write a as a self-delimiting number in . Set and check if . If so, move to Case 2, otherwise repeat Case .
Case 2
At position is a closing parenthesis, i.e., . In this cases we reached the end of a subtree with being the position of a corresponding open parenthesis. That means we have already computed all numbers for the whole subtree. Using an integer variable , sum up all the self-delimiting numbers in . Check if the sum exceeds . If it does write [math] as a self-delimiting number in and set , otherwise write in . Note that we store only one self-delimiting number between an open parenthesis and its matching closed parenthesis, and this number does not necessary occupy the whole space available. Hence, using operation we jump to the end of the space that is reserved for a number and start reading the second.
After writing the number we set . We end the algorithm if is out of , otherwise we check in which case we fall next and proceed with it.
Efficiency: To compute the set , we run two space-efficient DFS with bits in time (Lemma 2.7). The required -separator and -separator are computed in time (Corollary 3.27). We so get a balanced -separator in time for some constant .
The structures , , and the space-efficient DFS use bits. The size of the sets , , and are bound by and so use bits. Corollary 3.27 uses bits, which is the bottleneck of our space bound.
5 Iterator for Tree Decompositions using bits
We now introduce our iterator by showing a data structure, which we call tree-decomposition iterator. We think of it as an agent moving through a tree decomposition , one node at a time in a specific order. We implement such an agent to traverse in the order of an Euler-traversal and, when visiting some node in , to return the tuple with being the depth of the node .
The tree-decomposition iterator provides the following operations:
- •
: Initializes the structure for an undirected -vertex graph with treewidth .
- •
: Moves the agent to the next node according to an Euler-traversal and returns true unless the traversal of has already finished. In that case, it returns false.
- •
: Returns the tuple of the node where the agent is currently positioned.
We refer to initializing such an iterator and using it to iterate (call after every call of ) over the entire tree decomposition of a graph as iterating over a tree-decomposition of . Our goal in this section is to show that we can iterate over the bags of a tree decomposition by using bits and time for some constant . Recall that Reed’s algorithm computes a tree decomposition using separators. Assume we are given a separator in . The separator divides into several connected components, which we have to find for a recursive call. We refer to a data structure that implements the functionality of the lemma below as a connected-component finder. In the next lemma we use a choice dictionary [22, 24, 29] that manages a subset of a universe and provides—besides constant-time operations contains, add and remove—a linear-time iteration over .
Lemma 5.1**.**
Given an undirected -vertex -edge graph and a vertex set , there is an algorithm that iterates over all connected components of . The iterator is realized by the following operations.
- •
: Initializes the iterator.
- •
: Returns true exactly if there is another connected component left to iterate over. Returns false otherwise.
- •
: Returns the triple where is choice dictionary containing all vertices of a connected component, and .
The total runtime of all calls of next is unless returns false, and the running time of as well as of is . All operations use bits.
Proof 5.2**.**
The iterator is initialized by creating a bit array to mark all vertices that are part of connected components over which we already have iterated. To hold the current state of the iterator and answer a call of we store the triple (as defined in the lemma). Technically, to avoid modifications of the internal state of the iterator, we maintain a copy of that we return if is called.
If is called, we iterate over starting at vertex until we either find a vertex or reach (out of vertices). If exists, we have found a connected component whose vertices are not part of . We prepare a possible call of by computing new values for as follows: using a space-efficient BFS explore the connected component in starting at . Collect all visited vertices in a choice dictionary as well as add them to . Set and . Finally output true. Otherwise, does not exits and we reached , set and return false. For a call of return .
A choice dictionary can be initialized in constant time, thus as well as run in constant time. The operation has to scan for the vertex in time and runs a BFS in time to explore the connected component containing . However, the total time of all operation is since the operation continues the scan in from (the last found ) and avoids the exploration all vertices in , i.e., all already explored connected components. The bit array as well as the choice dictionary use bits.
To implement our tree-decomposition iterator we turn Reed’s recursive algorithm [36] into an iterative version. For this we use a stack structure called record-stack that manages a set of data structures to determine the current state of the algorithm. Informally, the record-stack allows us to pause Reed’s algorithm at specific time-points and continue from the last paused point. With each recursive call of Reed’s algorithm we need the following information:
- •
an undirected -vertex graph () of treewidth ,
- •
a vertex set , a separator , and
- •
an instance of the connected-component-finder data structure that iterates over the connected components of and that outputs the vertices of each component in a bit array.
We call the combination of these elements a record. Although we use a record-stack structure, often we think of it to be a combination of specialized stack structures: a subgraph-stack, which manages to store the recursive graphs used as a parameter for the call of Reed’s algorithm, a stack for iterating over the connected components of , called component-finder stack, a stack containing the separators as bit arrays, called -stack, a stack containing the vertex sets as bit arrays, called -stack. The bit arrays , and contain information referring to and are thus of size . On top of and we create rank-select data structures. Pushing a record to the record-stack is equivalent to pushing each element in to the corresponding stack (and analogous for popping).
Lemma 5.3**.**
When a record-stack is initialized for an undirected -vertex graph with treewidth such that each subgraph of on the subgraph-stack of contains 2/3 of the vertices of for and , then the record-stack occupies bits plus bits for the subgraph stack.
Proof 5.4**.**
Let be the number of edges in . We know that the size of the subgraph-stack structure is bits when the number of vertices of the subgraphs shrink with every push by a factor . Since each subgraph of has also a treewidth , the number of edges of each subgraph is bound by times the number of vertices. Thus, the subgraph stack uses bits.
The size of the bit arrays , (including the respective rank-select structures) and the component-finder is for . This means the total size of the stacks containing these elements is bits since they shrink in the same way as the vertex sets of the subgraphs. Storing the bag that is currently being output uses bits. Thus, the size of the record-stack without the subgraph stack is bits.
We call a tree decomposition balanced if has logarithmic height, and binary if is binary. Using our space-efficient separator computation for finding a balanced -separator we are now able to show the following theorem.
Theorem 5.5**.**
Given an undirected -vertex graph with treewidth , there exists an iterator that outputs a balanced and binary tree decomposition of width in Euler-traversal order using bits and time for some constant .
Proof 5.6**.**
We implement our tree-decomposition iterator by showing , and . We initialize the iterator for a graph with vertices, by initializing a flag , which indicates that the agent is not yet finished, and initialize a record-stack. The record stack is initialized by first initializing its subgraph stack with a reference to as the first graph . Next, we push the empty vertex set on the -stack in form of an initial-zero bit array of length . Now, using the techniques described in Lemma 4.1, we find a balanced -separator of and push it on the -stack. Then we create a new connected-component-finder instance (Lemma 5.1) and push on the component-finder stack.
We now view our implementation of , which has the task to calculate the next bag on the fly. Since the tree of our tree decomposition does not exist as a real structure, we only virtually move the agent to the next node by advancing the state of Reed’s algorithm. If , we return false (the agent can not be moved) and do not change the state of the record-stack. Otherwise, we first virtually move the agent and then return true.
Move to parent: If (we leave a leaf), we pop the record stack.
Otherwise, if the connected-component-finder instance has iterated over all connected components and the record-stack contains more than one record, we pop it. If afterwards the record-stack contains only one record, we set (the agent moved to the root from the rightmost child; the traversal is finished).
Move to next child: If has not iterated over all connected components (the agent is moving to a previously untraversed node), we use to get the next connected-component in , we push the vertex-induced subgraph on the subgraph stack as and proceed with one of the next two cases.
- •
If , we are calculating the bag of a leaf of by setting . We do this by pushing a bit array with all bits set to on the -stack and -stack and an empty component-finder on the component-finder stack.
- •
If , the agent is moving to a new internal node whose bag we are calculating as follows: we push a new bit array on the -stack. We then find the balanced -separator of and push it on the -stack. Then, create a new connected component-finder for and and push it on the component-finder stack and return true.
Anytime we pop or push a new record, we call the function of the subgraph stack to speed up the graph-access operations. This finishes our computation of .
To implement , we return the tuple with being the current bag, and being the number of records of the record-stack. The current bag is defined as . Thus, we iterate over all elements of via their rank-select data structures. Note that, since the subgraph on top of the record stack is toptuned, we can return the bag as vertices of or in time.
Efficiency: The iterator uses a record-stack structure, which occupies bits. Since the running time of Lemma 4.1 is , for some constant , and the input graphs are split in each recursion level into vertex disjoint subgraphs, the running time in each recursion level is . Summed over all recursion levels we get a running time of .
Make binary.* The balanced -separator partitions into any number of vertex disjoint sets between and such that no set contains more than of the vertices of (and ). The idea is to combine these vertex sets into exactly two sets such that neither contains more than vertices. For this we change our usage of the connected component finder slightly. After we initialize we also initialize two bit arrays and of size each with all bits set to [math]. We also store the number of bits set to for each of the bit arrays as and , i.e., the number of vertices contained in them (initially [math]). We now want to collect the vertices of all connected components of in and . While there are still connected components to be returned by , this is done by obtaining the size of the next connected component via as . If , we collect the next connected component in and increment . Otherwise, we do the same but for and . Doing this until all connected components are found results in and to contain all connected components of . For we implement a function that returns if it was not yet returned, or if it was not yet returned, or null otherwise. We store with the respective functions on the connected component finder stack (instead of ). Any time we do this during our iterator, the graph is toptuned, resulting in constant time graph access operations. The previous runtime and space bounds still hold.*
Often it is needed to access the subgraph induced by a bag of a tree decomposition for further computations. We call such a subgraph bag-induced. For this we show the following:
Lemma 5.7**.**
Given an undirected -vertex graph with treewidth and an iterator that iterates over a balanced tree decomposition of width we can additionally output the bag-induced subgraphs using bits additional space and additional time.
Proof 5.8**.**
*To obtain the edges we use a bit matrix of size whose bit at index is set to exactly if there exists an edge in . To quickly find the edges in we use together with rank-select data structures on and that allow us to map the vertices in , names as vertices of , to . We create anytime a new record is pushed on the record stack, and anytime is popped, we throw away . For reasons of performance relevant in Section 7, we want to avoid accessing vertices multiple times in a graph. Hence, when we push a bit matrix , we first use to initialize edges that were contained in and are still contained in . To obtain the edges of a vertex in , we can iterate over all the edges of in and check if the opposite endpoint is in . However, by using definition **(*TD2) of a tree decomposition, it suffice to iterate only over the edges of such a vertex once, i.e., the first time they are contained in a bag to create .
Efficiency: We can see that we store at most matrices this way since the record stack contains records (i.e., the height of the treedecomposition). Storing all bit matrices uses bits and initializing all bit matrices takes time, including initializing and storing the rank-select structures if not already present.
We conclude the section with a remark on the output scheme of our iterator. The specific order of an Euler-traversal encompasses many other orders of tree traversal such as pre-order, in-order or post-order. To achieve these orders we simply filter the output of our iterator, i.e., skip some output values.
6 Modifying the Record Stack to Work with
Bits
The space requirements of the record stack used by the iterator shown in Theorem 5.5 is bits. Our goal in Sect. 7 is to reduce it to bits. The bottleneck here is the record stack. Assume that, for , a graph with vertices is on top of the record stack. When considering the record on top of the record stack we see that most structures use bits: a separator , a vertex set and a connected component finder . The only structure that uses more space is the subgraph stack, which uses bits. This is due to the storage of the edge set using bits. The strategy we want to pursue is to store only the vertices of the subgraphs (but not the edges) such that the space requirement of the subgraph stack is bits. We call such a subgraph stack a minimal subgraph stack. In the following we always assume that the number of subgraphs on the minimal subgraph stack is and that the subgraphs shrink by a constant factor. This is in particular the case for the subgraphs generated by Reed’s algorithm.
In the following we make a distinction between complete and incomplete vertices. Only complete vertices have all their original edges, i.e., they have the same degree in the original graph as they do in the subgraph. Note that the number of incomplete vertices in each subgraph is , which follows directly from the separator size. To clarify, a vertex in the subgraph on top of the subgraph stack is incomplete exactly if it is contained in a separator of the parent graph .
Lemma 6.1**.**
Assume that we are given an undirected -vertex graph with treewidth and a toptuned minimal subgraph stack ( with . Assume further that each graph () has vertices, edges and contains incomplete vertices. The modified subgraph stack can be realized with bits and allows us to push an -vertex graph on top of a minimal subgraph stack in time. The resulting graph interface allows us to access the adjacency array of the complete vertices in constant time whereas an iteration over the adjacency list of an incomplete vertex runs in time.
Proof 6.2**.**
Recall that the subgraph stack considers every edge as a pair of directed arcs. Let be the vertex translation between and . Each complete vertex of has the same degree in both and . Thus, to iterate over all arcs of a complete vertex , iterate over every arc of and return the arc . For a complete vertex we can use the adjacency array of . To iterate over the arcs of an incomplete vertex (via support of adjacency lists of ), we differ two cases: (1) the arcs to a complete vertex and (2) the arcs to another incomplete vertex. To iterate over all arcs of (1) we iterate over all complete vertices of and check in if has an edge to . If it does, is an edge of . Thus, the iteration over all arcs incident to an incomplete vertex of (1) runs in time.
For the arcs according to (2), we use matrices storing the edges in between incomplete vertices. To build the matrices we proceed as follows. Whenever a new graph is pushed on the subgraph stack, we create a bit matrix of size and a rank-select data structure of size with exactly if is incomplete. is used to store the information if contains an edge between any two incomplete vertices and of , which is the case exactly if and . First, we initialize to contain only [math] for all bits. Then we use to find edges between incomplete vertices of and set the respective bits in to if those incomplete vertices are still contained in (if , we set all bits to [math]). Afterwards we are able to find edges between incomplete vertices that are already incomplete in the previous graph. We need to update to contain the information of the edges between the vertices that are complete in , but are not complete in . Since they are complete in , we can simply iterate over all complete edges of in time and check if both endpoints of are incomplete in via . If so, we set the respective bits in to .
Efficiency: Queries on () allow us to iterate over all arcs of (2) of an incomplete vertex in time. This results in a combined runtime of by ignoring zero-degree vertices in . Storing all bit matrices uses bits and the space used by the rank-select structures is negligible. The adjacency lists are realized by storing a pointer for each vertex. This uses negligible additional bits for implementing the interface. Our modified subgraph stack uses bits.
The last lemma allows us to store all recursive instances of Reed’s algorithm with bits. We use the result in the next section to show our first -bit iterator to output a tree decomposition on graphs of small treewidth.
7 Iterator for Tree Decomposition using Bits for
By combining the -bit iterator of Theorem 5.5 with the modified record stack of Lemma 6.1 we can further reduce the space to bits for a sufficiently small . Recall that the only structure using more than bits in the proof of Theorem 5.5 was the subgraph stack whose space is stated in Lemma 5.3. This allows us to show the following theorem.
Theorem 7.1**.**
Given an undirected -vertex graph with treewidth , there exists an iterator that outputs a balanced and binary tree decomposition of width in Euler-traversal order using bits and time for some constant . For with an arbitrary , our space consumption is bits.
Proof 7.2**.**
Recall that the tree decomposition iterator of Theorem 5.5 uses the algorithm of Theorem 3.26 to find vertex-disjoint paths for the construction of the separators.
With the construction of the vertex-disjoint paths we have to compute and access path storage schemes. To avoid querying the neighborhood of an incomplete vertex several times by querying the region with several times, we add the incomplete vertices to the boundary vertices when we build a path data structure. This neither increases our asymptotical time nor our total space bounds. Moreover, by adding the incomplete vertices to the boundary vertices and storing /, we avoid running a BFS on incomplete vertices and searching for the predecessor and successor of . If we afterwards construct a path, we iterate over the adjacency lists of incomplete vertices only once—even if regions are constructed several times.
The construction of a vertex-disjoint path involves reroutings. Recall that each rerouting extends a clean area by traversing over a path (two DFS) and then searches “backwards” within the area with two further DFS. Altogether, a rerouting can be done with DFS runs and DFS runs over all reroutings for one path .
To construct a balanced -separator a set is constructed by another DFS run. However, this iterates over the neighbors of each vertex only once. To sum up a balanced -separator can be constructed by DFS runs for some constant .
Given an -vertex, -edge graph stored in a minimal subgraph stack, we can run the space-efficient DFS of Lemma 2.7 in time and bits, which is a factor of slower than the DFS of Theorem 2.6. Thus, a balanced -separator can be constructed in time for some constant , which is the same time as stated in the proof of Theorem 5.5 (the constant here is larger). Concerning the space consumption note that we have a record-stack of size bits by Lemma 5.3 by using our minimal subgraph stack of bits. The extra values for and are negligible. Finally note that we can search a separator with bits by Corollary 3.27.
We next combine the theorem above with a recent tree-decomposition algorithm by Bodlaender et al. [10] as follows. The algorithm by Bodlaender et al. finds a tree decomposition for a given -vertex graph of treewidth in time for some constant [10]. The resulting tree decomposition has a width of . The general strategy pursued by them is to first compute a tree decomposition of large width and then use dynamic programming on that tree decomposition to obtain the final tree decomposition of width . For an overview of the construction, we refer to [10, p. 3]. The final tree decomposition is balanced due to the fact that its construction uses balanced -separators at every second level, alternating between an -balanced and an unbalanced -separator [10, p. 26]. Further details of the construction of different kinds of the final tree decomposition can be found in [10, p. 20, and p. 39]. Since its runtime is bounded by , it can write at most words and thus has a space requirement of at most bits.
Our following idea is to use a hybrid approach to improve the runtime of our iterator. We first run our iterator (Theorem 7.1). Once the height of the record-stack of our tree-decomposition iterator is equal to , the call of uses an unbalanced -separator . This ensures that the size of the bag is at most instead of . (We later add all vertices in the bag to all following bags.) Note that using a single unbalanced -separator on all root-to-leaf paths of our computed tree decomposition increases the height of the tree decomposition only by one. A following call of toptunes the graph and then uses Bodlaender et al.’s linear-time tree-decomposition algorithm [10] to calculate a tree decomposition of an vertex subgraph , which we then turn by folklore techniques into a binary tree decomposition by neither increasing the asymptotic size nor the width of the tree decomposition. In detail, this is done by repeatedly replacing all nodes with more than two children by a node with two children and , with , followed by adding the original children of to and , alternating between them both. To ensure property of a tree decomposition, we add the vertices in to all bags of . We so get a tree decomposition of the width .
Since contains vertices, the space usage of the linear-time tree-decomposition algorithm is bits. The runtime of the algorithm is bounded by . Once we obtain , we also need to transform each bag of since contains mappings in relation to , but we want them to contain mappings in relation to . This can be done in negligible time since was toptuned before. We then initialize a tree-decomposition iterator for as described in the beginning of Subsection 5. Now, as long as has not finished its traversal of , a call to on is equal to a call to on . Similarly, a call to on now returns the tuple with being the depth of in plus the size of the record stack of . Once iterator is finished, we throw away . Then, the operations of and work normally on until the size of the record stack again is or until the iteration is finished. Since we use our iterator only to recursion depth , our algorithm runs in time for some constant . The total runtime is for some constant .
Corollary 7.3**.**
There is an iterator to output a balanced binary tree-decomposition of width for an -vertex with treewidth in Euler-traversal order in time for some constant using bits. For and an arbitrary , the space consumption is bits.
If we try to run our iterator from the last corrolary on a graph that has a treewidth greater than , then either the computation of a vertex separator or the computation of Bodleander et al.’s algorithm for finding a tree decomposition [10] fails. In both cases, our iterator stops and we can output that the treewidth of is larger than .
8 Applications
From [16, Theorem 7.9] we know that there is an algorithm that solves all problems mentioned in Theorem 8.5 on an -vertex graph with treewidth in time for some constant when a tree decomposition with approximation-ratio is given. The general strategy used for solving these problems is almost identical. First, traverse the tree decomposition bottom-up and compute a table for each node . The table stores the size of all best possible solutions in the graph induced by all bags belonging to nodes below under certain conditions for the vertices in bag . E.g., for Vertex Cover the table contains solutions ( does belong or does not belong to the solution) and for Dominating Set it contains solutions (one additionally differs, if a vertex is already dominated or not). We only consider problems whose table has at most solutions for some constant . For each possible solution, the table stores the size of the solution and thus uses bits. After the bottom-up traversal, the minimal/maximal solution size in the table at the root is the solution for the minimization/maximization problem, respectively. An optimal solution set can be obtained in a top-down traversal by using the tables.
It is clear that, for large , we can not store all tables when trying to use bits. Our strategy is to store the tables only for the nodes on a single root-leaf path of the tree decomposition and for nodes with a depth less than some threshold value. The other tables are recomputed during the construction of the solution set. For a balanced tree decomposition this results in bits storing tables for vertices on a root-leaf path each of size bits. Using this strategy we have all information to use the standard bottom-up traversal to compute the size of the solution for the given problem for . To obtain an optimal solution set we need a balanced and binary tree decomposition with a constant-factor approximation of the treewidth.
As an example we first give an algorithm for vertex cover that can be easy generalized to other problems. We conclude this section by giving a list of problems that can be solved with the same asymptotic time and space bound.
A vertex cover of a graph is a set of vertices such that, for each edge , holds. For graphs with a small treewidth, one can find a minimum vertex cover by first computing a tree decomposition of the graph and then, using dynamic programming, calculate a minimal vertex cover. We start to sketch the standard approach.
Let be a tree decomposition of width of an undirected graph with treewidth . Now, iterate over in Euler-traversal order and, if a node is visited for the first time, calculate and store in a table all possible solutions of the vertex cover problem for . Also store the value of each solution, which is equal to the number of vertices used for the cover. If the solution is not valid, store instead.
When visiting a node and already exists, we update by using with being the node visited during the Euler-traversal right before ( is a child of ). The update process is done by comparing each solution in with each overlapping solution in . A solution is chosen if it has the smallest value among overlapping solution. The value of is added to the value of , and the two solutions are linked with a pointer structure. Two solutions and are overlapping exactly if, for each , is true. Once the Euler-traversal is finished, the table , with being the root of , contains the size of the minimum vertex cover of as the smallest value of all solutions. This is the first step of the algorithm.
The second step is obtaining , which is done by traversing top-down through all tables with the help of the pointer structures, starting at the solution with the smallest value in , and adding the vertices used by the solutions to the initially empty set if they are not yet contained in . For simplicity, we first only focus on Vertex Cover, but use a problem specific constant for the size of the tables and runtime of solving subproblems. For Vertex Cover . This allows us an easy generalization step to other problems subsequently. In the following the default base of is .
Lemma 8.1**.**
Given an -vertex graph with treewidth we can calculate the size of the optimal Vertex Cover of in time using bits for some constant .
Proof 8.2**.**
For an -vertex graph with treewidth and a given tree decomposition of width the runtime of the algorithm is . A table constructed for a bag consists of a bit array of size for each of the possible solutions, and their respective values and pointer structures. This uses bits per table for some problem specific constant , which is two for Vertex Cover. Thus, storing the tables for the entire tree decomposition uses bits. Our goal is to obtain the optimal vertex cover using only bits for both the tree decomposition and the storage of the tables. For obtaining only the size of , i.e., the first step of the algorithm, we only need to store the tables for the current root-node path of the tree decomposition iterator. The reason is that once a table has been used to update its parent table it is only needed for later obtaining the final cover via the pointer structures. We can iterate over a balanced binary tree decomposition of width in time using bits (Theorem 7.3) for some constant . To obtain the bag-induced subgraphs we use Lemma 5.7. We have to store tables, which results in bits used, which for equals bits (). Initializing and updating all tables can be done in time.
To obtain the final vertex cover we need access to all tables and bags they have been initially created for. We now use the previous lemma with modifications. Our idea is to fix some and to use partial tree decompositions of depths rooted at every th node of a root-leaf path. For this, let us define to be the partial tree decomposition where is a subtree of with root and depths .
Lemma 8.3**.**
Given an -vertex graph with treewidth for some constant we can calculate the optimal vertex cover of in time using bits for some constant .
Proof 8.4**.**
By iterating over we first compute the tables and pointer structures for where is the root of the tree decomposition of and where . Thus, the partial tree consists of nodes, each with a table of bits. (Recall that for Vertex Cover .)
We then start to follow the pointer structures starting from . When we arrive at a node having a table where the pointer structure is invalid (because the next table does not exist) we build . Afterwards, we can continue to follow the pointer structures since the next tables now exist. We repeat to build the partial tree decompositions anytime we try to follow a pointer that is invalid until we arrive at a leaf (at which point we backtrack). When we have built and processed all tables of a subtree with root , we can throw away all tables of . In other words, we have to store tables for only partial trees. Note that each partial tree uses tables of bits in total. Summed over all partial trees on a root-leaf path, we need to store bits. For this uses bits (). It remains to show the impact on the runtime. Anytime we want to obtain the tables of , we need to compute the tables of the subtree rooted by . Thus partial subtrees with deepest nodes need to be calculated -times, the partial subtrees above -times and so forth. This can be thought of as iterating over the tree decomposition of for times. In other words, we run the algorithm of Theorem 8.1, times.
We finally present our last theorem.
Theorem 8.5**.**
*Let be an n-vertex graph with treewidth for some constant . Using bits and time for some constant we can solve the following problems: Vertex Cover, Independent Set, Dominating Set, MaxCut and -Coloring. *
Proof 8.6**.**
As in the proof of Lemma 8.1 is the width of the tree decomposition. To change the previous two proofs to the different problems mentioned in Theorem 8.5 the only change is in the computation of the tables and the size of the tables. Thus, we have to take another value for the problem specific constant of the Lemma 8.1 and Lemma 8.3. For Vertex Cover, Independent Set and Max Cut the tables contain possible solutions and thus . For Dominating Set they contain and -Coloring they contain possible solutions, so and , respectively. For further details see [9].
The reference list from the paper itself. Each links out to its DOI / PubMed record.
- 1[1] Ravindra K. Ahuja, Thomas L. Magnanti, and James B. Orlin. Network Flows: Theory, Algorithms and Applications . Prentice Hall, 1993.
- 2[2] Eyal Amir. Approximation algorithms for treewidth. Algorithmica , 56(4):448–479, 2010. doi:10.1007/s 00453-008-9180-4 . · doi ↗
- 3[3] Tetsuo Asano, Taisuke Izumi, Masashi Kiyomi, Matsuo Konagaya, Hirotaka Ono, Yota Otachi, Pascal Schweitzer, Jun Tarui, and Ryuhei Uehara. Depth-first search using o(n) bits. In Proc. 25th International Symposium on Algorithms and Computation (ISAAC 2014) , volume 8889 of LNCS , pages 553–564. Springer, 2014. doi:10.1007/978-3-319-13075-0_44 . · doi ↗
- 4[4] Niranka Banerjee, Sankardeep Chakraborty, Venkatesh Raman, Sasanka Roy, and Saket Saurabh. Time-space tradeoffs for dynamic programming algorithms in trees and bounded treewidth graphs. In Proc. 21st International Conference on Computing and Combinatorics (COCOON 2015) , volume 9198 of LNCS , pages 349–360. Springer, 2015. doi:10.1007/978-3-319-21398-9\_28 . · doi ↗
- 5[5] Niranka Banerjee, Sankardeep Chakraborty, Venkatesh Raman, and Srinivasa Rao Satti. Space efficient linear time algorithms for BFS, DFS and applications. Theory Comput. Syst. , 62(8):1736–1762, 2018. doi:10.1007/s 00224-017-9841-2 . · doi ↗
- 6[6] Jérémy Barbay, Luca Castelli Aleardi, Meng He, and J. Ian Munro. Succinct representation of labeled graphs. Algorithmica , 62(1):224–257, 2012. doi:10.1007/s 00453-010-9452-7 . · doi ↗
- 7[7] Tim Baumann and Torben Hagerup. Rank-select indices without tears. In Proc. 16th International Symposium on Algorithms and Data Structures (WADS 2019) , volume 11646 of LNCS , pages 85–98. Springer, 2019. doi:10.1007/978-3-030-24766-9\_7 . · doi ↗
- 8[8] Hans L. Bodlaender. A linear-time algorithm for finding tree-decompositions of small treewidth. SIAM J. Comput. , 25(6):1305–1317, 1996. doi:10.1137/S 0097539793251219 . · doi ↗
