Space Efficient Algorithms for Breadth-Depth Search
Sankardeep Chakraborty, Anish Mukherjee, Srinivasa Rao Satti

TL;DR
This paper introduces space-efficient algorithms for the graph search method known as breadth-depth search, reducing space complexity significantly while maintaining near-linear time performance.
Contribution
The paper presents novel algorithms for breadth-depth search that operate in o(n log n) space, improving upon classical space bounds without substantial time penalties.
Findings
Achieved space complexity below n log n bits
Maintained linear time complexity for graph traversal
Successfully visited all vertices in correct order
Abstract
Continuing the recent trend, in this article we design several space-efficient algorithms for two well-known graph search methods. Both these search methods share the same name {\it breadth-depth search} (henceforth {\sf BDS}), although they work entirely in different fashion. The classical implementation for these graph search methods takes time and bits of space in the standard word RAM model (with word size being bits), where and denotes the number of edges and vertices of the input graph respectively. Our goal here is to beat the space bound of the classical implementations, and design space algorithms for these search methods by paying little to no penalty in the running time. Note that our space bounds (i.e., with bits of space) do not even allow us to explicitly store the required information to implement the…
Peer 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.
11institutetext: RIKEN Center for Advanced Intelligence Project,
1-4-1 Nihonbashi, Chuo-ku, Tokyo, Japan
11email: [email protected]
22institutetext: Chennai Mathematical Institute,
H1 SIPCOT IT Park, Siruseri, Chennai, India
22email: [email protected]
33institutetext: Seoul National University,
1 Gwanak-ro, Gwanak-gu, Seoul, South Korea
33email: [email protected]
Space Efficient Algorithms for Breadth-Depth Search
Sankardeep Chakraborty This work was partially supported by JST CREST Grant Number JPMJCR1402.11
Anish Mukherjee 22
Srinivasa Rao Satti 33
Abstract
Continuing the recent trend, in this article we design several space-efficient algorithms for two well-known graph search methods. Both these search methods share the same name breadth-depth search (henceforth BDS), although they work entirely in different fashion. The classical implementation for these graph search methods takes time and bits of space in the standard word RAM model (with word size being bits), where and denotes the number of edges and vertices of the input graph respectively. Our goal here is to beat the space bound of the classical implementations, and design space algorithms for these search methods by paying little to no penalty in the running time. Note that our space bounds (i.e., with bits of space) do not even allow us to explicitly store the required information to implement the classical algorithms, yet our algorithms visits and reports all the vertices of the input graph in correct order.
1 Introduction
Graph searching is an efficient and widely used bookkeeping method for exploring the vertices and edges of a graph. Given a graph , a typical graph search method starts with an arbitrary vertex in , marks as visited, and systematically explores other unvisited vertices of by iteratively traversing the edges incident with a previously visited vertex. The ordering in which the next vertex is chosen from an already visited vertex yields different vertex orderings of the graph. Two of the most popular and widely used graph search methods are depth-first search (DFS) and breadth-first search (BFS). BFS tries to explore an untraversed edge incident with the least recently visited vertex, whereas DFS tries to explore an untraversed edge with the most recently visited vertex. Both of these search methods have been successfully employed as backbones for designing other powerful and efficient graph algorithms. Researchers have also devised other graph search methods [9], and explored their properties to design efficient graph algorithms. For example, Tarjan and Yannakakis [17] introduced a graph search method, called maximum cardinality search (MCS) and used it to design a linear time algorithm for chordal graph recognition and other related problems.
Our focus here is to study another graph search method, namely breadth-depth search (BDS) from the point of view of making it space efficient. We note that, two very different graph search strategies exist in the literature, but surprisingly, under the same name. Historically, Horowitz and Sahni [13], in 1984, defined BDS and demonstrated its applications to branch-and-bound strategies. Henceforth we will refer to this version of BDS as after Horowitz and Sahni. Greenlaw, in his 1993 paper [12], proved that is inherently sequential by showing it is P-complete. Almost a decade later, Jiang [14], in 1993, defined another graph search method, under same name BDS, while designing an I/O- and CPU-optimal algorithm for decomposing a directed graph into its strongly connected components (SCC). In particular, he devised and used BDS (note that, this is different from [13] as we will see shortly) to give an alternate algorithm for SCC recognition. We will refer to this version of BDS as after Jiang. Implementing either of these algorithms takes time and bits of space in the standard word RAM model, where and denotes the number of edges and vertices of the input graph respectively. Our goal in this paper is to improve the space bound of the classical implementations without sacrificing too much in the running time.
1.1 Motivation and Related Work
Recently, designing space efficient algorithms has become enormously important due to their applications in the presence of fast growth of “big data” and the escalation of specialized handheld mobile devices and embedded systems that have a limited supply of memory i.e., devices like Rasberry Pi which has a huge use case in IoT related applications. Even if these mobile devices and embedded systems are designed with large supply of memory, it might be useful to restrict the number of write operations. For example, on flash memory, writing is a costly operation in terms of speed, and it also reduces the reliability and longevity of the memory. Keeping all these constraints in mind, it makes sense to consider algorithms that do not modify the input and use only a limited amount of work space. One computational model that has been proposed in algorithmic literature to study space efficient algorithms, is the read-only memory (ROM) model. Here we focus on space efficient implementations of BDS in such settings.
Starting with the paper of Asano et al. [1] who showed how one can implement DFS using bits in ROM, improving on the naive -bit implementation, the recent series of papers [2, 4, 6, 7, 8, 11] presented such space-efficient algorithms for a variety of other basic and fundamental graph problems: namely BFS, maximum cardinality search, topological sort, connected components, minimum spanning tree, shortest path, dynamic DFS, recognition of outerplanar graph and chordal graphs among others. We add to this small yet rapidly growing body of space-efficient algorithm design literature by providing such algorithms for both the BDS algorithms, and . In this process, we also want to draw attention to the fact that, even though these two search methods have same name, they work essentially in different manner. To the best of our knowledge, surprisingly this fact does not seem to be mentioned anywhere in the literature.
We conclude this section by briefly mentioning some very recent works on designing space efficient algorithms for various other algorithmic problems: Longest increasing subsequence [15], geometric computation [3] among many others.
1.2 Model of Computation and Input Representation
Like all the recent research that focused on designing space-efficient graph algorithms (as in [1, 2, 6, 7, 8, 15, 16]), here also we assume the standard word RAM model for the working memory with words size bits where constant time operations can be supported on -bit words, and the input graph is given in a read-only memory with a limited read-write working memory, and write-only output. We count space in terms of the number of bits in workspace used by the algorithms. Throughout this paper, let denote a graph on vertices and edges where . We also assume that is given in an adjacency array representation, i.e., an array of length where the -th entry stores a pointer to an array that stores all the neighbors of the -th vertex. For the directed graphs, we assume that the input representation has both in/out adjacency array for all the vertices.
1.3 Our Main Results and Organization of the Paper
We start off by introducing and in Sections 2 and 3 respectively as defined in [13] and [14] along with presenting their various space efficient implementations before concluding in Section 4 with some concluding remarks and future directions. Our main results can be compactly summarized as follows.
Theorem 1.1
Given a graph with vertices and edges, the and traversals of can be performed in randomized time111We use to denote logarithm to the base . using bits with high probabality (, for some fixed constant ), or time using bits, respectively.
1.4 Preliminaries
We use the following theorem repeatedly in our algorithms.
Theorem 1.2
[10]** Given a universe of size , there exists a dynamic dictionary data structure storing a subset of cardinality at most using space bits where denotes the size of the satellite data attached with elements of . This data structure can support membership, retrieval (of the satellite data), insertion, and deletion of any element along with its satellite data in time with probabality , for some fixed constant .
2 Breadth-depth Search of Jiang
A traversal of a graph walks through the vertices of and processes each vertex one by one according to the following rule. Suppose the most recently traversed edge is . If still has an unvisited edge, then select this edge to traverse. Otherwise choose an unvisited edge incident on the node most recently visited that still has unvisited edges. At this point (see line of the pseudocode for provided below) we also say that the node is being expanded. Note that a vertex might be visited many times via different edges and here we are only interested in the last visit to the vertex (in contrast to the BFS and DFS where only the first visit to the vertex is considered) when is expanded.
To implement this, more specifically to capture the fact of last visit, Jiang used an adaptive stack (abbreviated as adp-stack in the pseudocode below). A stack is called adaptive if pushing a node into the stack removes the older version of the node, if it was present in the stack earlier. We refer to the PUSH operation in an adaptive stack as ADPPUSH in the pseudocode. One way to implement an adaptive stack is via using a doubly linked list i.e., the algorithm stores the vertices in along with an array of pointers, one for each vertex, pointing to it’s location in . Now to push adaptively a vertex , we first insert into . Assuming it already belongs to , go to to update it so that it now points to the new location in , and delete the older entry from . Otherwise, is empty, and is now updated to the newly inserted location of . All of these can be done in time. Popping a vertex is straightforward as we have to delete the node from and update to NULL. We also maintain in a bitmap of size , call it visited, information regarding whether a vertex is visited or not. Then using all this auxiliary structure, it is easy to see that can be implemented in time using words or equivalently bits of space (becuase of storing the list and the array ). This concludes a brief description of as well as its implementation. Jiang also showed, using , how one can perform topological sort and strongly connected component decomposition. For detailed description, readers are referred to his paper [14]. Our focus here is to implement space efficiently.
In what follows, we illustrate a bit more on the inner working details of with the help of an example. Following the convention, as in the recent papers [1, 2], here also in we output the vertices as and when they are expanded (note that, if reporting in any other order is required, it can be done so with straighforward modification in our algorithms). Hence the root will be output at the very first step, followed by its rightmost child and so on. Towards designing space efficient algorithms for , we first note its similarities with DFS traversal method. Taking the graph of Figure 1(a) as running example where (say) the root is , and assuming that the adjacency list of every vertex is lexicographically sorted in the order of their labels, DFS would have put first in the stack, followed by pushing then and so on. As a result, these three vertices would come first in the output of DFS and so on. works in a slightly different manner. More specifically, pushes and into the stack (with at the bottom and at the top), and then expands (see the pseudocode for ). The node will again be discovered while expanding , and due to the adaptivity of the stack, the older entry of which was inserted into the stack due to the expansion of , will be removed (with a new entry of added to the stack). This phenomenon will be repeated again while expanding . Eventually will be discovered from and expanded. See the final tree in Figure 1(c). To enable expanding a vertex during the last visit (instead of the first visit which is the case for BFS and DFS), Jiang used the adaptive stack. As analyzed previously, the bottleneck factor in the space consumption of is the adaptive stack. Our main observation is that we can get rid of the adaptive stack and still perform traversal of the graph correctly. More specifically, in what follows we describe how to implement space efficiently using a standard stack (without the adaptive push operation), along with some bookkeeping, yet producing the same vertex ordering as Jiang’s .
2.1 Using Bits and Time
Note that, a vertex could be in one of the three states during the entire execution of the algorithm, (i) unvisited, (ii) expanded but not all of its children are expanded yet, and (iii) completed i.e., it is expanded as well as all of its children, if any. In our space efficient implementation of , we denote them by color white, grey and black respectively, and store this information using an array (say) of size bits. Along with this, we also store the last vertices that are grey in a (normal i.e., not adaptive) stack . We divide the stack into blocks of size vertices where the first block refers to the first set of vertex labels that are pushed into , the second block refers to the second bunch of vertex labels pushed into during and so on. Thus, there are blocks in total, and we always store the last block. Moreover, for every block we store the first and last element that are pushed in in a separate smaller sized stack . Thus, we need overall bits.
Now armed with these data structures, we start by marking the root as grey and pushing in . Note that, as and when a vertex gets expanded, i.e., turns grey, we can also output (i.e., report as the next vertex in order). At the next step, instead of inserting all of ’s white neighbors as in Jiang’s implementation, we insert only the rightmost white neighbor into the stack, and change its color (from white) to grey (see Figure 1(c)). Observe crucially that by delaying the insertion of other white neighbors at once, we are essentially removing the need of adaptivity from the stack as now elements are pushed only when they are expanded, not multiple times as in . Thus, we scan ’s adjacency list from right to left and insert the first white neighbor into the stack, mark it as grey in the array, and continue. We call this phase of the algorithm as forward step i.e., the phase in which we discover new vertices of the graph and insert them in . At some point during the execution of the algorithm, when we arrive at a vertex such that none of ’s neighbors are white, then we color the vertex as black, and we pop it from the stack. If the stack is still non-empty, then the parent of (in the tree) would be at the top of the stack, and we continue the from this vertex. On the other hand, if the stack becomes empty after removing , we need to reconstruct it to the state such that it holds the last grey vertices after all the pops done so far. We refer to the following phase of the algorithm as reconstruction step. For this, we basically repeat the same algorithm but with one twist which also enables us now to skip some of the vertices during this reconstruction phase. In detail, we again start with an empty stack, insert the root first and scan its adjacency list from the rightmost entry to skip all the black vertices and insert into the stack the rightmost grey vertex. Then the repeat the same for this newly inserted vertex into the stack until we reconstruct the last grey vertices. As we have stored the first and last vertices of each of the blocks in , we know when to stop this reconstruction procedure. Another equivalent way to achieve the same effect is to recolor all the grey vertices back to white, while retaining the colors of all the other (black and white) vertices, and repeat the same algorithm. It is not hard to see that this procedure correctly reconstructs the latest set of grey vertices in the stack . We continue this process until all the vertices become black. Obviously this procedure takes bits of space. To bound the running time, note that, whenever this procedure tries to reconstruct, vertices have changed their colors to black, and they are not going to be inserted again into the stack. As this can happen only for rounds, and since in each round we might spend time to scan the adjacency list and insert correct vertices into the stack, overall this procedure takes time. We conclude this section by mentioning that a similar kind of idea was used in [1] to provide space efficient DFS implementation, but we emphasize that ours algorithm is markedly different than [1] from the point of view of introducing delayed insertion of vertices into the stack, and thus removing the adaptivity from the stack, both the features not present in DFS. In what follows, we describe an improved algorithm generalizing the ideas developed in this section.
2.2 Using Bits and Time
In this section we first describe an algorithm that uses bits to performs in time with high probability, and modify it later to get an even improved algorithm. To obtain this, we first divide the stack into blocks of size vertices each. We group blocks into a super-block; thus there are super-blocks, each having vertices. For each vertex , we store its (a) color, (b) super-block ID (SID), if it is in , (and if it is not added to yet, i.e., if it is white), and (c) the number of groups of vertices that have been explored with as the current vertex. We also keep track of the first and the last element of each block, as well as super-block, and these takes up negligible (poly-logarithmic) space. We describe the algorithm below in detail.
The algorithm is similar to the algorithm of Section 2.1 with the following changes. The forward step remains mostly the same except updating the Items (b) and (c) above after every insertion of a vertex into the stack . More specifically, whenever a vertex is inserted into the stack, we store its SID in an array (Item (b) above), and also update the information regarding Item (c) above (also stored in a separate array). In addition, we store the nodes in the topmost two blocks of the top super-block of the stack. We also maintain the block IDs (BIDs) of all the vertices belonging to the topmost two super-blocks using the dictionary structure of Theorem 1.2.
The reconstruction step changes significantly as we cannot really afford time for the reconstruction of each super-block (like in Section 2.1); rather we would ideally like to spend time proportional to the size of the super-block, hence resulting in an optimal linear time algorithm. In order to achieve this, we do the following. As we have stored the first element of all the super-blocks, we can start by pushing that element (say ) into a temporary stack. We obtain the next vertex by determining (by consulting Item (c) above) the first grey vertex, say , that belongs to this super-block (as we can check from its SID) from the right endpoint of ’s adjacency array, and that is not already inserted in the current reconstruction procedure (can be checked from the dictionary structure of Theorem 1.2). Now we repeat the same in ’s list until we reconstruct the whole super-block. Note that, simultaneously we are also inserting the BIDs for every vertex in the structure of Theorem 1.2. We should mention one point at this time, the necessity of dynamic dictionary comes from the fact that we need to quickly find the BID information associated with the vertices in order to decide whether to insert any particular vertex in the stack or not. For performing this task very efficiently both time and space wise, having a simple array is not enough and thus, the requirement of more powerful dynamic dictionary structure. Due to the space limitations, we may need to discard all other blocks inside a super-block except the topmost two. Once we reconstruct the required blocks, the algoithm can proceed normally. Now all that is left is to determine the time and the space complexity of this procedure. Space requirement of our algorithm is bits which is dominated by the SID, topmost two blocks inside the top super-block and the dictionary structure.
To bound the number of reconstructions, note that, each time we reconstruct a super-block, the previous super-block’s vertices change their color to black and get popped from the stack, hence they will never be pushed again. Thus, the number of restorations (denoted by ) is bounded by . Now if the degree of a vertex is , then we spend time on searching for its correct neighbor in our algorithm due to the information stored in Item (c) above. To bound the running time of the algorithm, note that over reconstructions and over all vertices of degree at most , we spend time, and for vertices having degree larger than , over such reconstructions, we spend time. Observe that, this running time is randomized linear because of the use of dynamic dictionary222Our algorithm performs atmost insertion/deletion/retrieval during its entire execution using the dictionary of Theorem 1.2 which takes time with a probability of (where ) for each insertion/deletion/retrieval. Thus, the probability that our algorithm takes more than time is by union bound rule. of Theorem 1.2. This concludes the description of the algorithm taking randomized time and using bits with high probabality for some constant .
Before generalizing this algorithm, let us define some notations that are going to be used in what follows. The function is defined as applying the logarithm function on repeatedly times i.e., \lg\lg\ldots\textit{(k times)}\ldots\lg n. Similarly (also known as iterated logarithm) is the number of times the logarithm function is iteratively applied till the result is less than or equal to . It’s easy to see that is always a constant for any . Like the previous algorithm, this algorithm also uses the data structures of Item (a), (b), and (c) along with a hierarchy of levels (instead of just two levels like the previous algorithm). For some (which we will fix later), we set the size of -th level blocks as , and we divide the -th level blocks into -th level blocks. Thus, the number of -th level blocks inside a -th level block is , where means the smallest level blocks. We store the dynamic dictionary for -th level at the -th level for every , and the space required for storing the dictionary at level is given by bits. Through the entire execution of the algorithm, we always maintain the topmost two smallest level blocks along with other data structures. The forward step as well as the reconstruction step of the algorithm remains exactly the same other than modifying/storing informations at each level of the data structures suitably. As the work involved at each such level is simply one of the four operations from insertion/deletion/membership/retrieval (all takes time with high probability) at the dynamic dictionaries of the corresponding levels, by similar analysis as before, the final running time of the algorithm simply becomes times the overall number of levels of data structure that we maintain during the execution of the algorithm, and this can be bounded by . Also, we can bound the overall space requirement as bits. Now choosing , our algorithm takes bits of space and running time, and this concludes the description of the algorithm.
In what follows, we specially focus on designing space efficient algorithms for when the input graph is sparse (i.e., ). Studying such graphs is very important not only from theoretical perspective but also from practical point of view. These graphs appear very frequently in most of the realistic network scenario, like Road networks and the Internet, in real world applications.
2.3 Using Bits and Time
In this section, we show how one can obtain linear bits and linear time algorithm for for sparse graphs. For this we use the following lemma from [2].
Lemma 1
([2]) Given the adjacency array representation of a graph , using time, one can construct an auxiliary structure of size bits that can store a “pointer” into an arbitrary position within the adjacency array of each vertex. Also, updating any of these pointers takes time.
The idea is to store parent pointers into the adjacency array of each vertex using the representation of Lemma 1. More specifically, for an undirected graph, whenever the expands a vertex to reach following the edge , becomes the parent of in the tree, and at that time, we scan the adjacency array of to find and store a pointer to that position (within the adjacency array of ). For every vertex in , we can also store another pointer marking how far in ’s list has already processed. This pointer will start from the very end of every list, gradually moves towards the left, and at the end of the algorithm, will point to the first vertex of list. We also maintain color information in a bitmap of size bits. Given this pointer representation, it is easy to see how to implement in time. The main advantage of this algorithm of ours is, note that, we don’t even need to maintain any explicit stack to implement this process. We can extend similar idea for doing in directed graphs by setting up parent pointers (which are used during backtracking) in the in-adjacency list of every vertex and use the other pointer to mark progress in the out-adjacency list. With this, we complete the proof of Theorem 1.1 for .
3 Breadth-depth Search of Horowitz and Sahni
This version of BDS works as follows. The algorithm starts by pushing the root (i.e., the starting vertex) into the stack initially. At every subsequent step, the algorithm pops the topmost vertex of , and pushes all its unvisited neighbors into . See Figure 1(e) for an example. Note crucially that, due to the popping of the parent while pushing the children in , during backtracking the next vertex to be expanded is always at the top of the stack . This stack could grow to contain vertices, thus the classical implementation of this procedure takes time and bits of space. See [13] for a detailed description. In what follows, we show how to implement this space efficiently.
3.1 Using Bits and Time
To implement using bits, we crucially change the way we handle the stack during the execution of the algorithm. More specifically, we will not pop immediately the vertex which is going to be expanded at the very next step (as done in [13]), rather keep it in the stack instead for later use. We refer to this technique as the delayed removal of the vertices. Even though this is different than the delayed insertion technique (which was crucially used for ’s implementation), it is worth emphasizing that by introducing delayed removal of the vertices, the behaviour/operation of the stack in becomes pretty similar to the one in (as it will be clear from the next paragraph), thus we can reuse previously developed ideas for to obtain space efficient implementation of . In addition to this change, we use three colors as we did in the previous implementation with the exact same meaning attached to them, and store this information in an array . Also, we always store the last block of grey vertices of .
In detail, we start by marking the root, say , as visited, coloring it grey and inserting it into . This is followed by inserting all of ’s unvisited white neighbors into , change them to grey in . Now ’s rightmost child (say ) is at the top of the stack and we insert in all of ’s white neighbors without popping , also simultaneously marking them visited, and coloring as grey. This process is repeated until we arrive at the vertex all of whose neighbors are visited; at this point we make to be black and pop it from the stack. The vertex which is below in the stack (say ) is either its parent (if is the first child of its parent) or its previous sibling. We actually don’t know which case it is, but it does not matter – we simply continue the search from . The case when is the previous sibling of is handled the same way by the original algorithm as well as ours. In the case when is the parent of , all the other children of are colored black (since is the first child of ), and hence our algorithm colors as black and pops it from . Reconstructions are also handled in a similar fashion as in Section 2.1. I.e., we recolor the grey vertices back to white, and start executing the same algorithm from root but we don’t insert the black vertices again. This ensures that, if a vertex has become black already, its subtree will not be explored again, and once we restore the latest block of vertices, we start executing the normal algorithm. Clearly, we are using bits of space. Since the reconstruction happens only times, and each time we spend time, overall this procedure takes time. Generalizing this strategy by creating hierarchy of levels and then using dynamic dictionary at each levels like we did for in Section 2.2, we can similarly obtain an implementation of taking bits and time. This completes the description of the algorithms taking bits.
3.2 Using Bits and Time
We can use Lemma 1 to store parent pointers in the adjacency array of every vertex, and another pointer to mark the progress of so far in a similar way as we did for in Section 2.3. It is easy to see that with these structures, and additional color array, using time and bits, we can implement . One can also extend this to the directed graphs as metioned in Section 2.3. With this, we complete the proof of the Theorem 1.1 for .
4 Conclusions
We obtained space-efficient as well as time-efficient implementations for two graph search methods, both are known under the same name, breadth-depth search even though they perform entirely differently. The main idea behind our algorithm is the introduction of the delayed insertion and the delayed removal techniques for better managing the elements of the stack, and finally we use the classical blocking idea carefully to obtain the space-time efficient implementations. We think that these ideas might be of independent interest while designing similar space-time efficient algorithms for other existing graph search methods in the literature. We believe this is an important research direction as these search methods form basis of many important graph/AI algorithms.
We leave with two concrete open problems, is it possible to design a) space and polynomial time algorithms, and b) bits and time algorithms (deterministic or randomized) for both the BDS implementations? Another interesting direction would be to study these graph search methods in the recently introduced in-place [6] model where changing the input is also allowed in a restricted manner unlike the ROM model which is what we have focused in this paper.
The reference list from the paper itself. Each links out to its DOI / PubMed record.
- 1[1] T. Asano, T. Izumi, M. Kiyomi, M. Konagaya, H. Ono, Y. Otachi, P. Schweitzer, J. Tarui, and R. Uehara. Depth-first search using O ( n ) O 𝑛 \mathrm{O}(n) bits. In 25th ISAAC , pages 553–564, 2014.
- 2[2] N. Banerjee, S. Chakraborty, V. Raman, and S. R. Satti. Space efficient linear time algorithms for BFS, DFS and applications. Theory of Computing Systems , Jan 2018.
- 3[3] B. Banyassady, M. Korman, W. Mulzer, A. Renssen, M. Roeloffzen, P. Seiferth, and Y. Stein. Improved time-space trade-offs for computing voronoi diagrams. In 34th STACS , pages 9:1–9:14, 2017.
- 4[4] S. Chakraborty. Space Efficient Graph Algorithms . Ph D thesis. The Institute of Mathematical Sciences, HBNI, India, 2018.
- 5[5] S. Chakraborty, S. Jo, and S. R. Satti. Improved space-efficient linear time algorithms for some classical graph problems. Co RR , abs/1712.03349, 2017.
- 6[6] S. Chakraborty, A. Mukherjee, V. Raman, and S. R. Satti. A framework for in-place graph algorithms. In 26th ESA , pages 13:1–13:16, 2018.
- 7[7] S. Chakraborty, V. Raman, and S. R. Satti. Biconnectivity, st-numbering and other applications of DFS using O ( n ) O 𝑛 \mathrm{O}(n) bits. J. Comput. Syst. Sci. , 90:63–79, 2017.
- 8[8] S. Chakraborty and S. R. Satti. Space-efficient algorithms for maximum cardinality search, its applications, and variants of BFS. Journal of Combinatorial Optimization , Mar 2018.
