# Write a paragraph comparing Franceschini and Geffert paper to Quicksort and Heapsort.

Write a paragraph comparing Franceschini and Geffert paper to Quicksort and Heapsort. (minimum 350 words)

## Get Help With a similar task to - Write a paragraph comparing Franceschini and Geffert paper to Quicksort and Heapsort.

##### Additional Instructions:

An In-Place Sorting with O(n log n) Comparisons and O(n) Moves GIANNI FRANCESCHINI University of Pisa, Pisa, Italy AND VILIAM GEFFERT P. J. Šafárik University, Košice, Slovakia Abstract. We present the first in-place algorithm for sorting an array of size n that performs, in the worst case, at most O(n log n) element comparisons and O(n) element transports. This solves a long-standing open problem, stated explicitly, for example, in Munro and Raman [1992], of whether there exists a sorting algorithm that matches the asymptotic lower bounds on all computational resources simultaneously. Categories and Subject Descriptors: F.2.2 [Analysis of Algorithms and Problem Complexity]: Nonnumerical Algorithms and Problems—Sorting and searching General Terms: Algorithms Additional Key Words and Phrases: Sorting in-place 1. Introduction From the very beginnings of computer science, sorting has been one of the most fundamental problems, of great practical and theoretical importance. In virtually every field of computer science, there are problems that have the sorting of a set of objects as a primary step toward solution. (For early history of sorting, see Knuth [1973, Sect. 5.5].) It is well known that a comparison-based algorithm must perform, even in the average case, at least log n! ≈ n · log n − n · log e ≈ n · log n − 1.443n comparisons to sort an array consisting of n elements. (All logarithms throughout The research of G. Franceschini was partially supported by the Italian MIUR. The research of V. Geffert was supported by the Slovak Grant Agency of Science (VEGA) under contract “Combinatorial Structures and Complexity of Algorithms.” Authors’ addresses: G. Franceschini, Department of Informatics, University of Pisa, Largo B. Pontecorvo 3, 56127 Pisa, Italy, e-mail: francesc@di.unipi.it; V. Geffert, Department of Com- puter Science, P. J. Šafárik University, Jesenná 5, 04001, Košice, Slovakia, e-mail: geffert@upjs.sk. Permission to make digital or hard copies of part or all of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or direct commercial advantage and that copies show this notice on the first page or initial screen of a display along with the full citation. Copyrights for components of this work owned by others than ACM must be honored. Abstracting with credit is permitted. To copy otherwise, to republish, to post on servers, to redistribute to lists, or to use any component of this work in other works requires prior specific permission and/or a fee. Permissions may be requested from Publications Dept., ACM, Inc., 1515 Broadway, New York, NY 10036 USA, fax: +1 (212) 869-0481, or permissions@acm.org. C© 2005 ACM 0004-5411/05/0700-0515 $5.00 Journal of the ACM, Vol. 52, No. 4, July 2005, pp. 515–537. 516 G. FRANCESCHINI AND V. GEFFERT this paper are to the base 2, unless otherwise stated explicitly.) By Munro and Raman [1996b], the corresponding lower bound for element moves is �3/2 · n�. Concerning upper bounds for the number of comparisons, already the plain ver- sion of mergesort gets closely to the optimum, with fewer than n ·log n comparisons. However, this algorithm needs also an auxiliary array for storing n elements, it is not an in-place algorithm. That is, it does not work with only a constant auxiliary storage, in addition to the data stored in the input array. In-place algorithms play an important role, because they maximize the size of data that can be processed in the main memory without an access, during the computation, to a secondary storage device. The rich history of the comparisons-storage family of sorting algorithms, using O(n · log n) comparisons and, at the same time, O(1) auxiliary storage, begins with a binary-search version of insertsort. This algorithm uses fewer than log n!+n com- parisons, only a single storage location for putting elements aside, and only O(1) index variables, of log n bits each, for pointing to the input array. Unfortunately, the algorithm performs �(n2) element moves, which makes it unacceptably slow, as n increases. The heapsort [Floyd 1964; Williams 1964] was the first in-place sorting algorithm with a total running time bounded by O(n · log n) in the worst case. More precisely, it uses less than 2n · log n comparisons with the same O(1) storage requirements as insertsort, but only n · log n + O(n) moves, if the moves are organized carefully. Since then, many versions of heapsort have been developed; the two most important ones are bottom-up-heapsort [Wegener 1993] and a log∗-variant [Carlsson 1992]. Both these variants use not only the same number of moves as the standard heapsort, but even exactly the same sequence of element moves for each input. (See also the procedure “shiftdown” in Schaffer and Sedgewick [1993].) However, they differ in the number of comparisons. Though bottom-up variant uses only 3/2 · n · log n + O(n) comparisons, its upper bound for the average case is even more important; with n · log n + O(n) comparisons, it is one of the most efficient in-place sorting algorithms. The log∗-variant is slightly less efficient in an average, but it guarantees less than n · log n + n · log∗n comparisons in the worst case. For a more detailed analysis, see also Li and Vitányi [1993] and Schaffer and Sedgewick [1993]. Next in-place variants of a k-way mergesort came to the scene [Katajainen et al. 1996; Reinhardt 1992], with at most n · log n + O(n) comparisons, O(1) auxiliary storage, and ε · n · log n + O(n) moves. Instead of merging only 2 blocks, k sorted blocks are merged together at the same time. Here, k denotes an arbitrarily large, but fixed, integer constant, and ε > 0 an arbitrarily small, but fixed, real constant. Except for the first extracted element in each k-tuple of blocks, the smallest element is found with log k comparisons, if k is a power of two, since the k currently leftmost elements of the respective blocks are organized into a selection tree. Though log k is more than one comparison required in the standard 2-way merging, the number of merging sweeps across the array comes down to �log n/log k�, so the number of comparisons is almost unchanged. As an additional bonus, the number of element moves is reduced if, instead of elements, only pointers to elements are swapped in the selection tree. By the use of some other tricks, the algorithm is made in-place and the size of auxiliary storage is reduced to O(1). The early implementation of this algorithm, having such promising upper bounds, turned out to be unacceptably slow. It was observed that operations with indices representing the current state of the selection tree became a bottleneck of the program. Fortunately, the state of Sandra Yin Sandra Yin An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 517 a selection tree with a constant number of leaves can be represented implicitly, without swapping indices. This indicates that even by summing comparisons and moves we do not get the whole truth, the arithmetic operations with indices are also important. The k-way variant has been generalized to a (log n/log log n)-way in-place mergesort [Katajainen and Pasanen 1999]. This algorithm uses n · log n + O(n · log log n) comparisons, O(1) auxiliary storage, and only O(n · log n/log log n) el- ement moves. Since k is no longer a constant here, the information about the selec- tion tree is compressed, along with other information, into bits of (log n)-bit index variables by complicated bitwise operations. This increases several cost metrics, including the number of arithmetic operations. Therefore, the algorithm is mainly of theoretical interest, as the first member of the comparisons-storage family breaking the bound �(n · log n) for the number of moves. The family of algorithms sorting with O(n) element moves and O(1) auxiliary storage is not so numerous. The first algorithm of this type is selectsort, which is a natural counterpart of insertsort. Carefully implemented, it sorts with at most 2n − 1 moves, a single location for putting one element aside, and O(1) index variables. Unfortunately, it performs also �(n2) comparisons. As shown in Munro and Raman [1996b], O(n2) comparisons and O(1) indices suffice for reduction of the number of moves to the lower bound �3/2 · n�. Another improvement is a generalized heapsort [Munro and Raman 1992]: This method is based on a heap in which internal nodes have �n1/k� children, for a fixed integer k. The corresponding heap tree is thus of constant height, which results in an algorithm sorting with O(n) moves, O(1) storage, and O(n1+ε) comparisons. Finally, consider the family of algorithms sorting with O(n · log n) comparisons and O(n) element moves. The first member is a so-called tablesort [Knuth 1973; Munro and Raman 1992]. We use any algorithm with O(n · log n) comparisons but, instead of elements, we move only indices pointing to the elements. When each element’s final position has been determined, we transport all elements to their destinations in linear time. However, this algorithm requires �(n) auxiliary indices. The storage requirements have been reduced to O(nε) by a variant of samplesort [Munro and Raman 1992]. The same result can also be obtained by the in-place variant of the k-way mergesort [Katajainen et al. 1996; Katajainen and Pasanen 1999], mentioned above, if k = �nε�. This reduces the number of merging sweeps to a constant, which results in O(n · log n) comparisons and O(n) element moves. Such modification is no longer in-place, as it uses O(nε) auxiliary indices to represent a selection tree. We leave the details to the reader. No previously published sorting algorithm uses, in the worst case, O(n · log n) comparisons, O(n) moves, O(1) auxiliary storage, and, at the same time, O(n·log n) arithmetic operations. This ultimate goal has only been achieved in the average case [Munro and Raman 1992]. In the worst case, the algorithm uses �(n2) comparisons but, for a randomly chosen permutation of input elements, the probability of this worst case scenario is negligible. It was generally conjectured, for many years, that an algorithm matching simulta- neously the asymptotic lower bounds on all above computational resources does not exist. For example, in Raman [1991], it was proved that the algorithm with O(n1+ε) comparisons using generalized heaps is optimal among a certain restricted family 518 G. FRANCESCHINI AND V. GEFFERT of in-place sorting algorithms performing O(n) moves. It was hoped that, by gener- alizing from a restricted computational model to all comparison-based algorithms, we could get a higher trade-off among comparisons, moves, and storage. 1.1. OUR RESULT. The result we present in this article contradicts the above conjectures and closes a long-standing open problem. We shall exhibit the first sorting algorithm that reduces, simultaneously, the number of comparisons, moves, and storage. Our algorithm operates, in the worst case, with at most 2n · log n + o(n · log n) element comparisons, (13 + ε) · n element moves, and O(1) auxiliary storage, for each n ≥ 1. Here, ε > 0 denotes an arbitrarily small, but fixed, real constant. The number of auxiliary arithmetic operations with indices is O(n · log n). We can slightly reduce the number of moves, to (12+ε) ·n, with a modified version that uses 6n · log n + o(n · log n) comparisons. The algorithm was born as a union of the ideas contained in two independent technical reports [Geffert 2002; Franceschini 2003]. We believe that, besides the theoretical breakthrough achieved by its analysis, the algorithm can also be of practical interest, because of its simplicity. 1.2. ALGORITHM IN A NUTSHELL. Using an evenly distributed sample a1, . . . , a f of size �(n/(log n)4), split the remaining elements into some segments σ0, σ1, . . . , σ f , of length �((log n)4) each, so that all elements in the segment σk satisfy ak ≤ a ≤ ak+1. The sorted array is obtained by forming the sequence σ ′0, a1, σ ′ 1, . . . , a f , σ ′ f , where σ ′ k denotes the segment σk in sorted order. To sort σk , use a modified heapsort, based on a heap structure in which the internal nodes have �((log n)4/5) sons. This results in a constant number of moves per each element extracted from the heap. Since an evenly distributed sample is hard to find, the sample grows dynamically, as the computation demands. Initially, the sample is empty, with f = 0. That is, all elements are transported, one after another, into the segment σ0. Whenever, in the course of the computation, some segment σk becomes “too large,” halve this segment into two segments of equal length. The median element of the original segment is inserted in the proper position of the sample, so that the sample remains sorted. To minimize the number of moves required for insertions in the sample, the sample is sparsely distributed in a block of size �(n/(log n)3), in such a way that we do not lose the advantage of a quick binary search over the sample elements. Whenever required, a local density of the sample elements in the block is eliminated by redistributing the sample more evenly, which does not happen “too often.” To avoid the corresponding rearrangement of segments, we use also a pointer structure, connecting each sample element ak with the corresponding segment σk . That is, only pointers are moved, the segments stay motionless in a separate workspace. However, an in-place algorithm does not have an additional buffer array of size 3n, required for the sample and the segments, nor P ≈ �(n/(log n)2) bits, required for pointers. The bits are “created” at the very beginning by a modified heapsort. This initial routine collects the smallest and the largest P elements into blocks �L and �R placed, respectively, at the beginning and at the end of the array, which leaves an unsorted block A′ in between. Then, the j th bit can be encoded by swapping the j th element in �L with the j th element in �R. To “create” a buffer for sorting the block A′ of length n′, select the element b� of rank �n′/4� and partition A′ into blocks A< and B≥, using b� as a pivot. Then sort Sandra Yin An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 519 the block A<, using B≥ as an empty buffer array. (We can test if a given location contains a buffer element, by a single comparison with b � . Before an “active” element is moved to a new location, one buffer element escapes from this location to the current location of the “hole.”) After sorting A< we iterate, focusing on B≥ as a new block A′. After O(log n) iterations, we are done. 2. Sorting with Additional Memory Before presenting our in-place algorithm, we concentrate on a simpler task. We are going to sort a given contiguous block A, consisting of m elements, using only O(m · log m) comparisons and O(m) element moves. As some additional resources, we are given a buffer memory, of size at least 3m−1, that can be used as a temporary workspace, and a pointer memory, capable of containing at least �4m/(log m)2� bits. To let the elements move, we also have a hole, that is, one location, the content of which can be modified without destroying any element. An assignment a j := ai transports not only one element from the location i to j , but also the hole from j to i . At the very beginning, the hole is in a single extra location. 2.1. BUFFER MEMORY. The buffer memory forms a separate contiguous block B, initially consisting of at least 3m−1 buffer elements. All buffer elements are greater than or equal to a given buffer separator b � , placed in an extra location, while all elements in A are strictly smaller than b � . During the computation, the elements of A and B are mixed up. However, by a single comparison with b � , we can test whether any given location contains a buffer element, or an active element, a subject of sorting, placed originally in A. The buffer memory B consists of two parts. First, there is a low level segment memory, a sequence of segments allocated dynamically from the right end of B and growing to the left, as the computation demands. All allocated segments are of the same fixed length. Second, there is a fixed high level frame memory, placed at the left end of B. 2.2. STRUCTURE OF THE SEGMENT MEMORY. All segments are of a fixed length s, where s = {�(log m)4� �(log m)4� + 1 so that s is odd. (1) During the computation, the number of active segments never exceeds s#, defined by s# = �2m/s� ≤ 2m/(log m)4, (2) and hence the size of workspace reserved for the segment memory is bounded by S = s# · s ≤ 2m . (3) Here we assume that m is “sufficiently large,” such that s ≤ m, and hence s# ≥ 2. We shall later discuss how to handle a block A that is “short.” Initially, all segments are free, containing buffer elements only. The algorithm keeps the starting position of the last segment that has been allocated in a global 520 G. FRANCESCHINI AND V. GEFFERT index variable �s. Initially, �s points to the right end of the buffer memory B. To allocate a new segment, the procedure simply performs the operation �s := �s−s, and returns the new value of �s as the starting position of the new segment. Immediately after allocation, some �s/2� active elements (smaller than b�) are transported to the first �s/2� positions of the new segment. The corresponding buffer elements are saved in the locations released by the active elements. From this point forward, the segment becomes active. In general, the structure of an active segment is c1 · · · chbh+1 · · · bs , where c1 · · · ch are active elements stored in the segment, while bh+1 · · · bs are some buffer ele- ments. The value of h is kept between �s/2� and s −1, so that at least one half (roughly) of elements in each active segment is active, and still there is a room for storing one more active element. Neither c1 · · · ch nor bh+1 · · · bs are sorted. In addition, the algorithm does not keep any information about the boundary h separating active and buffer elements, if the segment is not being manipulated at the present moment. However, since all active elements are strictly smaller than b � and all buffer elements are greater than or equal to b � , we can quickly determine the number of active elements in any given segment, using a binary search with b � over the s locations of the segment, which costs only 1 + �log s� ≤ O(log log m) comparisons, by (1). 2.3. STRUCTURE OF THE FRAME MEMORY. The frame memory, placed at the left end of B, consists of r# so-called frame blocks, each of length r , where r = 1 + �log(2m/s)� ≤ 2 + log(2m/8) = log m , r# = 2r−1 = 2�log(2m/s)� ≤ 2 · 2m/s ≤ 4m/(log m)4, (4) using (1) and m ≥4. That is, the frame memory is of total length R = r# · r ≤ 4m/(log m)3. (5) Using (3) and m ≥ 4, we get that the total space requirements for the segment and frame memories do not exceed the size of the buffer B, since R + S ≤ 4m/ (log m)3 + 2m ≤ 3m − 1. A frame block is either free, containing buffer elements only, or it is active, containing some active elements followed by some buffer elements. Initially, all frame blocks are free. During the computation, active frame blocks are concentrated in a contiguous left part of the frame, followed by some free frame blocks in the right part. However, there are some important differences from the segment memory structure: First, the active elements, forming a left part of a frame block, are in sorted order. So are the active frame blocks, forming a left part of the frame mem- ory. More precisely, let a1, a2, . . . , a f denote the sequence of all active elements stored in the frame memory, obtained by reading active elements from left to right, ignoring buffer elements and frame block boundaries. Then, a1, a2, . . . , a f is a sorted sequence of elements. Consequently, a subsequence of these, stored in the first (leftmost) positions of active frame blocks, denoted here by ai1, ai2, . . . , aig , must also be sorted. Here f denotes the total number of active elements in the frame, while g the number of active frame blocks, at the given moment. Similarly, ai j ai j +1ai j +2 · · · ai j+1−1, the sequence of active elements stored in the j th frame block, is also sorted. An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 521 Second, the number of active elements in an active frame block can range between 1 and r−1. That is, we keep room for potential storing of one more active element in each active frame block, but we do not care about a sparse distribution of active elements in the frame. The only restriction follows from the fact that there are no free blocks in between some active blocks. 2.4. RELATIONSHIP BETWEEN THE FRAME AND SEGMENTS. Each active element in the frame memory, that is, each of the elements a1, a2, . . . , a f , has an associated segment σ1, σ2, . . . , σ f in the segment memory. The segment σk , for k ranging between 1 and f , contains some active elements satisfying ak ≤ a ≤ ak+1, taken from A and stored in the structure so far. The active elements satisfying a f ≤a are stored in σ f , similarly, those satisfying a ≤ a1 are stored in a special segment σ0. Note that the segment σ0 has no “parent” in the sequence a1, a2, . . . , a f , that is, no frame element to be associated with. Chronologically, σ0 is the first active segment that has been allocated. If f = 0, that is, no active elements have been stored in the frame yet, all active elements are transported from A to σ0. Note also that (in order to keep the number of active elements in active segments balanced) we do allow some elements equal to ak be stored both in σk−1 and in σk . In general, we may even have ak = ak+1 = · · · = ak ′ , for some k < k ′. Then elements equal to ak may be found in any of the segments σk−1, σk, . . . , σk ′ . However, the algorithm tries to store each “new” active element a, coming from A, in the leftmost segment that can be used at the moment, that is, it searches for k satisfying ak <a ≤ak+1. Recall that we also maintain the invariant that each active segment contains at least �s/2� active elements. Thus, if the frame contains f active elements at the given moment, namely, a1, a2, . . . , a f , for some f ≥1, the total number of active elements, stored both in the frame and the segments σ0, σ1, σ2, . . . , σ f , is at least f + ( f +1) · �s/2�. Now, using the fact that s is odd, by (1), we get that this number is at least f + ( f + 1) · (s/2−1/2) = ( f + 1) · s/2 + ( f/2−1/2) ≥ ( f + 1) · s/2. However, the total number of all active elements is exactly equal to m, which gives m ≥ ( f + 1) · s/2, and hence also f + 1 ≤ 2m/s. But f + 1, the number of active segments, is an integer number, which gives that f +1 ≤ �2m/s�. Therefore, using (2) and (4), f + 1 ≤ �2m/s� = s# , f ≤ �2m/s� ≤ 2�log(2m/s)� = r# . (6) (The argument has used the assumption that f ≥1. However, (6) is trivial for f = 0, since r# ≥ s# ≥ 2, if m is sufficiently large.) As a consequence, we get that f + 1, the number of active segments, does not exceed s#, the capacity of the segment memory. Second, f , the number of active elements in the frame, will never exceed r#, the total number of blocks in the frame, and hence there is enough room to store all active frame elements, even if each active frame block contained only a single element of the sequence a1, a2, . . . , a f . 2.5. STRUCTURE OF THE POINTER MEMORY. The relative order of active frame elements in the sequence a1, a2, . . . , a f does not correspond to the chronolog- ical order, in which the segments σ0, σ1, σ2, . . . , σ f are allocated in the segment memory. Therefore, with each element position in the frame, we associate a pointer to the starting position of corresponding segment. More precisely, if the frame is 522 G. FRANCESCHINI AND V. GEFFERT viewed as a single contiguous zone of elements x1 . . . xR (ignoring boundaries between the frame blocks), then the corresponding zone of pointers is π1 . . . πR . If, for some �, the element x� is a buffer element, then π� = 0, which represents a NIL pointer. Conversely, if x� is an active element belonging to the sequence a1, a2, . . . , a f , then the value of π� represents the starting position of the segment associated with x�. (The pointer π0 to the segment σ0, having no “parent” in the frame, is stored separately, in a global index variable.) Since there are at most s# segments, all of equal length, a pointer to a segment can be represented by an integer value ranging between 0 and s# = �2m/s� ≤ m/2, using (2). Thus, a single pointer can be represented by a block of p bits, where p = 1 + �log s#� ≤ log m . (7) The number of pointers is clearly equal to R, the total size of the frame. Therefore, p# = R . Thus, the pointer memory can be viewed as a contiguous array consisting of p# bit blocks, of p bits each, and hence, by (5), its total length is at most P = p# · p = R · p ≤ �4m/(log m)2� , (8) using also the fact that P must be an integer number. Since an in-place algorithm can store only a limited amount of information in index variables, the pointer memory is actually simulated by two separate contigu- ous blocks �L and �R, each containing at least �4m/(log m)2� elements. Initially, �L and �R are sorted, and the largest (rightmost) element in �L is strictly smaller than the smallest (leftmost) element in �R. This allows us to encode the value of the j th bit, for any j ranging between 1 and �4m/(log m)2�, by swapping the j th element of �L with the j th element of �R. Testing the value of the j th bit is thus equivalent to comparing the relative order of the corresponding elements in �L and �R, which costs only a single comparison. Setting a single bit value requires a single comparison and, optionally, a single swap of two elements, that is, 3 element moves. The initial distribution of elements in �L and �R represents all �4m/(log m)2� bits cleared to zero. 2.6. INSERTING ELEMENTS IN THE STRUCTURE. The procedure sorting the block A works in two phases. In the first phase, the procedure takes, one after another, all m active elements from A and inserts them in the structure described above. The procedure also saves some buffer elements from B, and keeps the struc- ture “balanced.” In the second phase, all active elements are transported back to A, this time in sorted order. For each active element a in A, we find a segment, among σ0, σ1, σ2, . . . , σ f , where this element should go. First, by the use of a binary search with the given element a over ai2, . . . , aig , that is, over the leftmost locations in the active frame blocks, find the “proper” frame block for the element a, that is, the index j satisfying ai j < a ≤ ai j+1 . Note that the element ai1 is excluded from the range of the binary search. If a ≤ai2 , the binary search will return j = 1, that is, the first frame block. Similarly, for aig <a, the binary search returns j = g, that is, the last frame block. If g < 2, we can go directly to the first (and only) active frame block without using any binary search, that is, j :=1. An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 523 Second, by the use of a binary search with the given element a over the r lo- cations in the j th active frame block, find the “proper” active frame element for the element a, that is, the index k satisfying ak < a ≤ ak+1. Note that, since ai j < a ≤ ai j+1 , the elements ak and ak+1 are between ai j and ai j+1 in the sequence a1, a2, . . . , a f of all frame elements, not excluding the possibility that ai j = ak , and/or ak+1 = ai j+1 . Recall that the j th active frame block begins with the active elements ai j ai j +1ai j +2 . . . ai j+1−1, followed by some buffer elements, to fill up the room, so that the length of the block is exactly equal to r . These buffer elements are not sorted, however, they are all greater than or equal to b � , the smallest buffer element. On the other hand, the element a, being active, is strictly smaller than b � . This allows us to use the binary search with the given a in the standard way, which returns the index k satisfying ak < a ≤ ak+1. For ai j+1−1 < a, the binary search returns correctly k = i j+1−1. If j = 1, that is, if we are in the first frame block, the binary search may end up with k = 0, indicating that a ≤ a1 = ai1 . Third, let the active frame element ak , satisfying ak < a ≤ ak+1, be placed in a position � of the frame memory, that is, ak = x�. (For k = 0, we take � := 0.) Then read the information from π� in the pointer memory and com- pute the starting position of the segment σk . This segment contains elements ranging between ak and ak+1. If k = 0, that is, the element a should go to σ0, the starting position of the segment is obtained from a separate global index variable. Fourth, by the use of a binary search with the buffer separator b � over the s lo- cations in the current segment, find the boundary h dividing the segment into two parts, namely, c1 · · · ch , the active elements stored in the segment, and bh+1 · · · bs , some buffer elements, filling up the room. Fifth, save the buffer element bh+1 aside, to the current location of the hole, and, after that, store the given element a in the segment. If h + 1 < s, we are ready to insert the next element from A. However, if h + 1 = s, the current segment cannot absorb any more elements. Therefore, if the segment has become full, we call a procedure “rebalancing” the structure before trying to store the next element. This procedure will be described later, in Section 2.9. The above process is repeated until all m active elements have been inserted in the structure. Initially, the procedure allocates the segment σ0, and stores the first s−1 active elements directly in σ0, without travelling via the frame. The number of moves for these elements is the same as in the standard case, that is, two moves per each inserted element. Let us now determine the standard cost of inserting a single element. The binary search looking for a proper frame block inspects a range consisting of g−1 < r# elements, and hence it performs at most 1 + �log r#� ≤ log m comparisons, by (4). The second binary search, looking for a proper active element within the given frame block, inspects a range of r elements, performing at most 1 + �log r� ≤ O(log log m) comparisons, using (4). Reading the value encoded in the pointer π� requires p≤ log m element comparisons, by (7). The binary search with b� over the s locations in the current segment uses 1 + �log s� ≤ O(log log m) comparisons, by (1). Finally, saving one buffer element and transporting the element a to the current segment can be performed with 2 element moves. However, these costs do not include rebalancing. Since m elements are inserted this way, we get: 524 G. FRANCESCHINI AND V. GEFFERT LEMMA 2.1. If we exclude the costs of rebalancing, inserting m elements in the structure requires 2m · log m + O(m · log log m) comparisons and 2m moves. 2.7. EXTRACTING IN SORTED ORDER—FRAME LEVEL. In the second phase, the active elements are transported back to A, in sorted order. Let fm denote the maximal value of f , corresponding to the number of active elements in the frame at the moment when the last active element has been stored in the struc- ture. Thus, the frame memory contains the sorted sequence of active elements a1, a2, . . . , a fm, intertwined with some buffer elements, so the total size of the frame is R, consisting of elements x1 · · · xR . Then, we have active elements in the segments σ0, σ1, σ2, . . . , σ fm, with σk containing active elements that satisfy ak ≤ a ≤ ak+1. Thus, to produce the sorted order of all active elements, it is suf- ficient to move, back to A, the sequence σ ′0, a1, σ ′ 1, a2, σ ′ 2, . . . , a fm, σ ′ fm , where σ ′k denotes the block of sorted active elements contained in σk . The procedure begins with moving the block σ ′0 to A. (We shall return to the problem of sorting a given segment σk below, in Section 2.8.) Then, in a loop iterated for � = 1, . . . , R, check whether x� is an active element. This requires only a single comparison, comparing x� with b � . If x� is a buffer element, it is skipped, we can go to the next element in the frame. If x� is an active element, that is, x� = ak , for some k, the procedure saves the leftmost buffer element, not moved yet from the output block A, in the current location of the hole and, after that, moves x� = ak to A. (The first free position in A, that is, the position of the leftmost buffer element, is kept in a separate global index variable, and incremented each time a new active element is transported back to A.) Then, we read the value encoded in the pointer π� and compute the starting position of the segment σk . After that, we move all active elements contained in σk to A, in sorted order, by the procedure presented in Section 2.8. Before showing how the segment σk can be sorted, let us derive computational costs of the above procedure, not including the cost of sorting σk . Testing whether x� is an active element, for � = 1, . . . , R, requires R ≤ O(m/(log m)3) comparisons, by (5). Transporting x� = ak to A requires only 2 fm element moves in total, since only active elements are moved. This gives 2 fm ≤ 2r# ≤ O(m/(log m)4) element moves, by (6) and (4). Reading the values of fm pointers, of length p bits each, can be done with fm · p ≤ r# · p ≤ O(m/(log m)3) comparisons, using (6), (4), and (7). Summing up, we have: LEMMA 2.2. If we exclude the costs of sorting the segments, extracting in sorted order requires O(m/(log m)3) comparisons and O(m/(log m)4) moves. 2.8. EXTRACTING IN SORTED ORDER—SEGMENT LEVEL. Now we can describe the routine extracting, in sorted order, all active elements contained in the given segment σk . Let hk denote the number of active elements in σk . Clearly, hk ≤ s ≤ �(log m)4� + 1, using (1). Initially, the routine determines the value of hk by the use of a binary search with b � over the s locations of the segment. This costs 1 + �log s� ≤ O(log log m) comparisons. After that, the routine uses a generalized version of heapsort, which in turn uses a modified heap-like structure, with t = �(log m)4/5� root nodes (instead of a single root node), and with internal nodes having t sons An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 525 (instead of two sons). More precisely, we organize c1 · · · chk , the active elements contained in the segment, into the implicit structure with the following properties: First, the father of the node ce is the node ce′ , where e′ = �(e−1)/t�, provided that e′ ≥1. If e′ <1, then ce is one of the root nodes. This implies that the heap has t roots, and that the sons of ce are the nodes ct ·e+1, ct ·e+2, . . . , ct ·e+t . If, for some e and d < t , we have t · e + d = hk , the corresponding node ce has only d sons, instead of t . A leaf is a node ce without any sons, that is, with t · e≥hk . Before passing further, note that the heap does not have more than five levels, since, by travelling to a root from chk , we get h(1) = �(hk −1)/t� < hk/t , h(2) = ⌊(h(1)−1)/t⌋ < hk/t2, h(3) = ⌊(h(2)−1)/t⌋ < hk/t3, h(4) = ⌊(h(3)−1)/t⌋ < hk/t4, h(5) = ⌊(h(4)−1)/t⌋ ≤ h(4)/t−1/t < hk/t5−1/t ≤ s/t5−1/t5. If we had 1 ≤ h(5), then 1 < s/t5 −1/t5, and hence also t5 < s −1. Now, using t = �(log m)4/5� and s ≤�(log m)4� + 1, by (1), we would obtain �(log m)4/5�5 < �(log m)4�, which is a contradiction. To see this, note that, for each real x > 0, �x4/5�5 ≥ x4. But �x4/5�5 is an integer number, and hence �x4/5�5 ≥ �x4�. The second property of our heap is that, if a node contains an active element, then this element is not greater than any of its sons. Note that we do not care about sons of a node containing a buffer element. (Initially, there are no buffer elements in the heap. However, when some active elements have been extracted, buffer elements will fill up the holes.) This heap property is established in the standard way: For e = �(hk−1)/t�, . . . , 1, establish this property in the positions e, . . . , hk . This only requires to determine whether ce is not greater than the smallest of its sons and, if necessary, swap the smallest son with ce. Processing a single node this way costs t comparisons and 3 element moves. After that, the heap property is re-established for the son just swapped in the same way. This may activate a further walk, up to some leaf. Taking into account that there are h(1) nodes with paths of lengths 1, 2, 3, or 4 (starting from the given node and ending in a leaf), h(2) nodes with paths of lengths 2, 3, or 4, h(3) nodes with paths of lengths 3 or 4, and h(4) nodes with paths of length 4, we get that building the heap costs t · ∑4i=1 h(i) < 2hk comparisons and 3 · ∑4i=1 h(i) < 6hk/(log m)4/5 moves. After building the heap, the routine transports, hk times, the smallest element from the heap to the output block A. Here the moves are organized as follows. First, save the leftmost buffer element, not moved yet from A, in the current location of the hole. Then, find the smallest element, placed in one of the t roots, and move this element to A. After that, find the smallest element among the t sons of this root, and move this element to the node corresponding to its father. Iterating this process at most five times, we end up with a hole in some leaf. Now, we are done. The hole in the leaf will be filled up by a buffer element in the future, as a side effect. (Usually, in the next iteration, extracting the next smallest element from the heap.) Thus, unlike in the standard version of heapsort, the size of the heap does not shrink but, rather, some new buffer elements are inserted into the heap structure, filling up the leaf holes. These buffer elements are then handled by the extracting 526 G. FRANCESCHINI AND V. GEFFERT routine in the standard way, as ordinary active elements. Since these elements may travel down, from the leaf level closer to the root level, a node containing a buffer element may have a son containing a smaller buffer element. This will do no harm, however, since each buffer element is strictly greater than any active element, because of the buffer separator b � . Thus, no buffer element can be extracted from the heap as the smallest element in the first hk iterations, when the routine terminates. Deriving the costs of the above routine is straightforward. The routine repeats hk iterations, performing each time at most 5(t −1) ≤ 5(log m)4/5 comparisons and 6 moves, since the heap has at most five levels. This gives hk · 5(log m)4/5 comparisons and hk · 6 moves. Now we can sum the costs of sorting the segment σk . Determining the value of hk costs O(log log m) comparisons. Building the heap costs at most 2hk comparisons and 6hk/(log m)4/5 moves. Extracting active elements in sorted order costs hk · 5(log m)4/5 comparisons and hk · 6 moves. Summing up, we get hk · O((log m)4/5) comparisons and hk · (6/(log m)4/5 + 6) moves. To obtain the total cost of sorting all segments σ0, σ1, σ2, . . . , σ fm, we use the fact that ∑ fm k=0 hk ≤ m, since the number of active elements stored in the segments is bounded by the total number of active elements. Therefore, the sum over all segments results in the following upper bounds: LEMMA 2.3. Sorting all segments does not require more than O(m · (log m)4/5) comparisons or 6m + O(m/(log m)4/5) moves. Alternatively, we could use the heap structure with parameter t = �log m�. This results in a heap with four levels, instead of five (since �x�4 ≥ �x4�, for each real x > 0). This reduces the leading factor for the number of moves from 6m to 5m. The price we pay is increasing the number of comparisons, from o(m · log m) to 4m · log m + O(m). The detailed argument is very similar to the proof for t = �(log m)4/5�. 2.9. REBALANCING AT THE SEGMENT LEVEL. This procedure is activated by the routine of Section 2.6, inserting a new active element in the structure, when, for some k, the segment σk has become full, having absorbed s active elements. At the moment of activation, some global index variable is pointing to the starting position of σk . The procedure also remembers �, the position of the associated active element ak = x� in the frame memory, as well as j , the position of the frame block containing the element ak . We shall call this block the current frame block. (If σk = σ0, that is, k = 0, there is no associated element in the frame. Then, � = 0, but we still have the current frame block, namely, j = 1.) The above indices were computed when the latest active element was inserted in the structure. First, by the use of a binary search with the buffer separator b � over the r locations in the current frame block, find �′, the position of the leftmost buffer element in this block. We shall denote this element by b . Recall that we maintain the invariant that each active frame block has a room for one more active element, and therefore it does contain at least one buffer element. Second, find a median in the segment σk , that is, an element a of rank �s/2�+1. Without loss of efficiency, the selection procedure will position a at the end of σk . Third, the median a is inserted in the current frame block, one position to the right of ak . The active elements lying in between ak and b , that is, occupying locations x�+1 · · · x�′−1 in the frame memory, are shifted one position to the right. An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 527 At the same time, b is saved from x�′ to the location released by a at the end of the segment σk . (As a special case, if ak is the rightmost active element in the current frame block, only b and a are swapped. The same holds when σ0 is rebalanced for the first time, with � = 0 and �′ = 1.) Since a has been picked from σk , it satisfies ak ≤ a ≤ ak+1, and hence the sequence of active elements stored in the frame memory remains sorted. Fourth, after shifting the active elements in the locations x�+1 · · · x�′−1 one posi- tion to the right, we have to shift the corresponding pointers π�+1 · · · π�′−1 as well, so the active elements remain connected with their segments. To move an integer pointer value from πe to πe+1, we only have to read the value encoded in πe and, at the same time, clear πe, and then to encode this value in πe+1. Such transport of a pointer costs O(p) comparisons and moves. Fifth, we need to connect a new active element in the frame with a new seg- ment. This concerns the element a , now placed in x�+1. Thus, we allocate a new segment σ and encode its starting position in the pointer π�+1. Sixth, the full segment σk is halved, that is, we place some �s/2� active elements greater than or equal to a into the left part of σ and collect the remaining �s/2� active elements, smaller than or equal to a , in the left part of the original segment σk . Since many elements may be equal to a , we distribute such elements both to σk and σ , so that their active parts are of equal lengths. This also requires to save �s/2� buffer elements, placed originally in σ , to the locations released in σk . (We shall give more details below, in Section 2.10.) The outcome of halving is that the active elements in σk are split into two segments σk and σ , satisfying ak ≤a ≤a and a ≤a ≤ak+1, respectively. Seventh, if there is still a room for storing one more active element in the current frame block, the structure has been rebalanced. We are done, ready to take the next element from A. However, if this block has become full, because of a , the program control jumps to a routine rebalancing the frame level, described later, in Section 2.11. Let us now derive the computational costs. The binary search, determining the position of the leftmost buffer element in the current frame block, inspects a range of r elements, performing 1+�log r� ≤ O(log log m) comparisons, by (4). Finding a median, in a segment of length s, requires only O(s) ≤ O((log m)4) comparisons and ε · s ≤ ε · (2 + (log m)4) element moves, where ε>0 is an arbitrarily small, but fixed, real constant, by Geffert and Kollár [2001] and (1). Rearranging the elements a , b , and x�+1 · · · x�′−1 in their locations can be done with at most r +2 ≤ O(log m) moves, by (4). Shifting the pointers π�+1 · · · π�′−1 one position to the right costs O(r · p) ≤ O((log m)2) comparisons, by (4) and (7), together with the same number of moves. Encoding the starting position of a new segment in the pointer π�+1 requires O(p) ≤ O(log m) element moves, by (7). Halving the active elements in σk into two segments σk and σ requires only O(s) ≤ O((log m)4) comparisons and 3/2·s ≤ 3/2·(2 + (log m)4) moves, using Lemma 2.5, displayed in Section 2.10 below, and (1). By summing the bounds above, we get that a single activation of the procedure rebalancing a segment performs O((log m)4) comparisons and (3/2 + ε) · (log m)4 moves. Taking into account that each activation increases the number of active segments, that we start with one segment, namely, σ0, and that we end up with fm + 1 segments, we see that the number of activations is bounded by fm. This value is bounded by fm ≤ s# ≤ 2m/(log m)4, using (6) and (2). This gives: 528 G. FRANCESCHINI AND V. GEFFERT LEMMA 2.4. The total cost of keeping the segment level balanced is O(m) comparisons and (3 + ε) · m moves, where ε>0 is an arbitrarily small, but fixed, real constant. 2.10. HALVING A SEGMENT. Here, we describe a simple procedure for halving, needed in Section 2.9 above. We are given a segment σk of size s, and a median a , that is, an element of rank �s/2� + 1, put aside. We want to place some �s/2� active elements greater than or equal to a into the left part of another given seg- ment σ , of size s again, and collect the remaining �s/2� elements smaller than or equal to a in the left part of σk . The first �s/2� buffer elements of σ must be saved. In the first phase, with s−1 comparisons and no moves, we count c′, the number of elements strictly smaller than a , in σk . This gives us c = �s/2�−c′, the number of elements equal to a that should remain in σk . This number will be required in the second phase, when each element a of σk is compared with a twice, using “a <a ” and “a >a .” The elements strictly smaller than a and the first c elements detected to be equal to a will be considered “small,” while the remaining equal elements and those strictly greater than a will be “large.” Each time an element a = a will be detected, the counter c will be decreased by one, until it gets to zero. From then on, any “new” element a will be considered “small” if and only if a <a , and “large” otherwise. In the second phase, the configurations of the segments are σk = A1 U B1b and σ = A2 B2, where A1 and A2 denote, respectively, the active elements of σk found to be “small” or “large,” collected so far, B1 the buffer elements moved from σ to σk , B2 the elements of σ not moved yet, U the elements of σk not examined yet, and b a single buffer element, filling up the room. A2 and B1 are of equal length, not exceeding �s/2�. Initially, σk = Ub , σ = B2, with A1, A2, and B1 empty. The procedure also remembers the current position of the hole. (After the first iteration, the hole is always in the leftmost location of B1.) The second phase proceeds in a loop, as follows. Using at most two compar- isons, the rightmost element a of U is determined to be “small” or “large.” If a is large, we save the leftmost element from B2 in the current location of the hole and fill up the new hole in B2 by a. Thus, A2 and B1 have been extended, while U and B2 have been reduced. If a is small, we scan U from left to right until we find the first element a′ that is large. All elements on the left of a′ become a part of A1, without being moved. Since a is a small element placed on the right of the position �s/2�, a′ must be found before we reach the position �s/2� + 1, or else we would have more than �s/2� small elements, which is a contradiction. Now we save the leftmost element from B2 to the hole, fill up the hole in B2 by a′, and move a to the place released by a′. Then, all necessary boundaries are updated. This is repeated until we have transported exactly �s/2� active large elements from σk to σ . As a consequence, the remaining �s/2� active elements of σk , placed on the left of B1, must be all small, since the rank of a is �s/2� + 1 and s is odd, by (1). Clearly, we have used at most 3s comparisons in total, and at most three moves per each large element moved from σk to σ . This gives: LEMMA 2.5. Given a median a , a segment of size s can be halved with at most O(s) comparisons and 3/2 · s moves. An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 529 2.11. REBALANCING AT THE FRAME LEVEL. This routine is activated by the procedure of Section 2.9, rebalancing a segment, when it finds out that, for some j , the j th frame block has become full, having absorbed r active elements. As a side effect, the routine may increase the number of active blocks in the frame. The routine is based on a new variant of the well-known data structure (see Itai et al. [1981] and Willard [1982]), used to maintain a set of elements in sorted order in a contiguous zone of memory. For the purpose of keeping the frame memory balanced, the frame consisting of r# frame blocks is viewed, implicitly, as a complete binary tree with r# = 2r−1 leaves, and hence of (edge) height r−1. We introduce the following numbering of levels: i = 0 for the leaves, 1 for their fathers, and so on, ending by i = r−1 for the root. Each node of the tree is associated with a contiguous subarray of the frame blocks, and with a path leading to this node from the root, as follows: The j th leaf, for any j ranging between 1 and 2r−1, is associated with the j th frame block, that is, with a subarray consisting of 1 = 20 frame blocks, starting from the block position j . The corresponding path from the root to this leaf is represented by the number � = j −1. It is easy to see that by reading the binary representation of � from left to right (with leading zeros so that its length is r −1) we get the branching sequence along this path; 0 is interpreted as branching to the left, while 1 as branching to the right. Given a node v at a level i , associated with a path number � and with a subarray of length 2i blocks, starting from a block position j , the father v ′ of this node is associated with the path number � ′ = � �/2�, and with the subarray of length 2i+1, starting from the block position j ′ = j , if � is even (v is a left son of v ′), but from j ′ = j −2i, if � is odd (right son). Thus, the subarray for the father is obtained by concatenation of the two subarrays for its sons, while its path number by cutting off the last bit in the path number for any of its sons. During the computation, the number of active elements in some local area of the frame may become too large. The purpose of rebalancing a subarray, associated with a node v at a level i , for i > 0, is to eliminate such local densities and redistribute active elements more evenly. More precisely, after rebalancing the subarray, the following two conditions will hold: (i) The number of active elements, in any frame block belonging to the subarray associated with the given node v at the level i , will not exceed the threshold τi = r −i . (ii) The frame memory will not contain any free blocks (without active elements) in between some active blocks. Note that, if a node v at a level i >0 is an ancestor of the j th leaf, the condition (i) ensures that the j th frame block is not full any longer. Neither is any other block within the subarray. Such redistribution of active elements is possible only if α(v), the total number of active elements in the subarray associated with v , is bounded by α(v) ≤ τi · 2i. We say that the node v overflows, if α(v) > τi · 2i. The condition (ii) is required only because of the procedure presented in Sec- tion 2.6, transporting active elements from the block A to the structure. Recall that this procedure uses a binary search over the leftmost locations in the active frame blocks, and hence these blocks must form a contiguous zone. Now we can describe the routine rebalancing the frame. 530 G. FRANCESCHINI AND V. GEFFERT First, starting from the father of the frame block that is full, climb up and find the lowest ancestor v that does not overflow, with α(v) ≤ τi · 2i. The formulas for j and � , presented above, give us a simple tool for computing the boundaries of the associated subarrays, along the path climbing towards the root. To compute the value of α(v), for the given ancestor v at the given level i , simply scan all 2i frame blocks forming the associated subarray and sum up the numbers of active elements in these blocks, using a binary search with the buffer separator b � over the r locations in each block. Second, move the α(v) active elements in the associated subarray of v to the last α(v) locations. That is, processing all 2i · r locations in the subarray from the right to left, collect all elements smaller than b � to the right end. Before moving an active element from xe to xe′ , for some e<e′, the buffer element in the target position xe′ is saved to the current location of the hole. Then move the associated pointer in the corresponding positions of the pointer memory, from πe to πe′ , by reading and clearing the bit value encoded in πe and encoding this value in πe′ . Third, redistribute the α(v) active elements back, this time more evenly in the 2i frame blocks of the subarray, moving also the pointers in the corresponding posi- tions, as follows. Let αD = �α(v)/2i� and αM = α(v) mod 2i . Then, put αD+1 active elements in each of the first αM blocks, and αD active elements in each of the remain- ing 2i −αM blocks. In each block, the active elements are concentrated in its left part. Fourth, as a side effect of redistribution, the size of the active part in the frame memory may have been increased. This requires to update the value of g, the number of active frame blocks, kept in a separate global index variable. Let g′ be the block position of the rightmost frame block in the subarray of v . Then, let g := max{g, g′}. It should be pointed out that, for each leaf, the desired ancestor v without overflow does exist. Using (6), (4), and τi = r−i , for the level i = r−1, that is, for v being the root node, we get α(v) = f ≤ r# = 1 · 2r−1 = τr−1 · 2r−1, and hence at least the root node does not overflow. Therefore, in the first step, the loop climbing up towards the root must halt correctly. Further, the redistribution of active elements, presented in the third step, is correct, since (αD + 1) · αM + αD · (2i −αM) = α(v). It is easy to see that the redistribution satisfies the condition (i) above, using the fact that the node v does not overflow, and hence α(v) ≤ τi · 2i. There are two cases to consider: For α(v) ≤ τi · 2i −1, we have αD + 1 ≤ �(τi · 2i −1)/2i� + 1 ≤ (τi −1) + 1 = τi , since τi is an integer. If α(v) = τi · 2i, we get αM = 0, and hence all 2i blocks are “remaining,” with only αD active elements in each. But here αD = �(τi · 2i )/2i� = �τi� = τi . It is also easy to see that the redistribution satisfies the condition (ii). Since v is the lowest ancestor that does not overflow, along the path from the full frame block towards the root, it must have a son that does overflow, with at least τi−1 ·2i−1 active elements in its subarray. (As a special case, for i = 1, we get τ0 ·20 = (r−0) ·1 = r active elements in the j th frame block that is full.) The subarray of the son is a part of the subarray associated with v , and hence α(v) ≥ τi−1 · 2i−1 ≥ 2 · 2i−1, using the fact that i −1 ≤ r −2. But then αD = �α(v)/2i� ≥ 1. This implies that each frame block in the subarray associated with v contains at least one active element after redistribution, and hence the zone of active frame blocks will remain contiguous. Consider now the cost of a single activation of the above routine, rebalancing a subarray for a node v at a level i . Looking for the lowest ancestor without overflow An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 531 requires to count the numbers of active elements in the associated subarrays along a path climbing up from a father of a leaf, for levels e = 1, . . . , i . In the eth level, 2e blocks are examined, by a binary search over the r locations of the block. By (4), this gives ∑i e=1 2 e · (1 + �log r�) ≤ 2i · O(log log m) comparisons. The cost of the second step, collecting α(v) active elements to the right end, is 2i · r comparisons (one comparison with b � for each location in the subarray), plus α(v) · 2 + 1 moves (two moves per each collected element). However, with each collected element, the corresponding pointer must also be transported, which gives additional α(v) · O(p) comparisons and moves. Using α(v) ≤ τi · 2i ≤ r · 2i, together with (4) and (7), the cost of the second step can be bounded by 2i · O(r · p) ≤ 2i · O((log m)2) comparisons and moves. The same computational resources are sufficient in the third step, redistributing the same number of active elements back, but more evenly, together with their pointers. Again, this gives α(v) · O(p) comparisons and moves, which can be bounded by 2i · O((log m)2). Finally, the fourth step does not require any element comparisons or moves, it just updates one index variable, in O(1) time. Summing up, the cost of a single activation is 2i · O((log m)2) comparisons and moves, for each node v at the fixed level i >0. To get the total cost, we must take into account how frequently such rebalancing is activated. When a rebalancing is activated, v must have a son with at least τi−1 · 2i−1 active elements, since v is the lowest ancestor that does not overflow, along some path climbing up. Now, trace back the history of computation, to the moment when the entire subarray associated with v was a subject of redistribution for the last time. This way we get a node v ′, either an ancestor of v or v itself, at a level i ′ ≥ i , with the associated subarray containing the entire subarray for v . After the redistribution for v ′, both sons of v contained at most τi ′ · 2i−1 ≤ τi · 2i−1 active elements. Thus, in the meantime, the number of active elements in one of the sons of v has been increased by at least τi−1 · 2i−1−τi · 2i−1 = 2i−1. Since other redistributions, taking place between the moments of rebalancing v ′ and v , could not “import” any active elements to the subarray of v from any other parts of the frame, the 2i−1 additional active elements must have been inserted here. (See the procedure of Section 2.9, third step). Thus, there have to be at least 2i−1 insertions in the associated subarray between any two redistributions for v . Note that, for the fixed level i , subarrays associated with different nodes v do not overlap. Thus, we can charge the cost of each activation, for the given node v , to the 2i−1 insertions preceding this activation in the given subarray, without charging the same insertion more than once. This gives 2i · O((log m)2)/2i−1 ≤ O((log m)2) comparisons and moves, per a single insertion of an active element in the frame memory. Since, in the whole computation, there were only fm ≤ r# ≤ O(m/(log m)4) insertions, by (6) and (4), we get the cost O(m/(log m)2) comparisons and moves, for rebalancing of all nodes at the fixed level i . By summing over all levels, using i ≤ r −1 ≤ log m, by (4), we get the total cost: LEMMA 2.6. The total cost of keeping the frame memory balanced is O(m/ log m) comparisons, together with the same number of moves. 2.12. SUMMARY. By summing the bounds presented in Lemmas. 2.1–2.4 and 2.6 above, we get: 532 G. FRANCESCHINI AND V. GEFFERT THEOREM 2.7. The cost of sorting the given block A of size m is 2m · log m + O(m · (log m)4/5) comparisons and (11+ε) ·m moves, where ε>0 is an arbitrarily small, but fixed, real constant, provided we can use additional buffer and pointer memories, of respective sizes 3m−1 and �4m/(log m)2�. The algorithm presented above assumes that m is “sufficiently large,” so that s, defined by (1), satisfies s ≤m. This presupposition holds for each m >216 = 65536. Shorter blocks are handled in a different way, by the procedure described later, in Section 3.3. The bounds presented by Theorem 2.7 for the number of comparisons and moves will remain valid. 3. In-Place Sorting Now we can present an in-place algorithm sorting the given array A consisting of n elements. If n ≤ 216, the array is sorted directly, by the procedure of Section 3.3, described later. In the general case, for n > 216, the task of the main program is to provide sufficiently large pointer and buffer memories for the procedure presented in Section 2. 3.1. BUILDING A POINTER MEMORY. The size of the largest block ever sorted by the procedure of Section 2 will not exceed m = n/4. Using (8) and the fact that the function 4x/(log x)2 is monotone increasing for x ≥8, we see that the size of the pointer memory can be bounded by P = �4(n/4)/(log(n/4))2� = �n/(log(n/4))2�. This will suffice for all sorted blocks. The pointer memory is built by collecting two contiguous blocks �L and �R. The block �L, placed at the left end of A, will contain the smallest P elements of the array A, while �R, placed at the right end, the largest P elements. The block �R is created first, by the use of the heapsort with t root nodes and internal nodes having t sons. The detailed topology of edges connecting nodes in this kind of heap has been presented in Section 2.8, devoted to extracting sorted elements at the segment level. However, there are some substantial differences from the generalized heapsort of Section 2.8. This time the branching degree is t = �log n�. Therefore, the heap has q ≤ 1 + �logt n� ≤ O(log n/ log log n) levels. Here we keep large elements at the root level, instead of small elements. That is, no node contains an element smaller than any of its sons. Unlike in Section 2.8, no buffer elements are used here to fill up the holes, the heap structure shrinks in the standard way, when the largest element is extracted. The initial building of the heap structure is standard, and agrees with the heap building in Section 2.8. It is easy to see that, for a heap with n elements, branching degree equal to t , and q levels, the cost of the heap initialization can be bounded by t ·∑q−1i=1 n/t i < n·t/(t−1) ≤ O(n) comparisons and 3·∑q−1i=1 n/t i < n·3/(t−1) ≤ O(n/ log n) moves, using t ≥ log n. After building the heap, the routine extracts, P times, the largest element from the heap in the standard way. That is, when the largest element is extracted, it replaces the element in the rightmost leaf, which in turn is inserted into the “proper” position along the so-called special path, starting from the position of the largest root (just being extracted) and branching always to the largest son. The costs of the above routine are straightforward. The trajectory of the special path can be localized with q · (t −1) comparisons, and the new position for the An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 533 element in the rightmost leaf can be found by a binary search along this trajectory with 1 + �log q� comparisons. Summing up, an extraction of the largest element can be done with q · (t−1)+ (1+�log q�) comparisons, together with q +2 moves. Using t ≤ O(log n) and q ≤ O(log n/ log log n), we get, per a single extraction, at most O((log n)2/ log log n) comparisons, together with O(log n/ log log n) moves. If we let the above procedure run till the end, it would sort the entire array A in time O(n · (log n)2/ log log n). However, the execution is aborted as soon as the largest P elements are collected. Since P ≤ O(n/(log n)2), the cost of building the heap becomes dominant, and hence the block �R is created with O(n) comparisons and O(n/ log n) moves. After �R, the block �L is created in the same way, with the same computational needs of comparisons and moves. Instead of large elements, here we collect the smallest P elements. In addition, since �L should be created at the left end of A, all indices are manipulated in a mirrorlike way, seeing the first position to the left of �R as the beginning of the array. LEMMA 3.1. Building the pointer memory requires O(n/ log n) moves and O(n) comparisons. Now the configuration of the array A has changed to �LA′�R, where A′ denotes the remaining elements, to be sorted. Before proceeding further, the algorithm verifies, with a single comparison, whether the largest (rightmost) element in �L is strictly smaller than the smallest (leftmost) element in �R. If this is not the case, all elements in A′ must be equal to these two elements. Therefore, the algorithm terminates, the entire array A has already been sorted. Conversely, if �L and �R pass the test above, they can be used to imitate a pointer memory consisting of P bits. 3.2. PARTITION-BASED SORTING. When the blocks �L and �R have been cre- ated, the zone A′ is kept in the form ASAU, where AS and AU represent the sorted and unsorted parts of A′, respectively. Each element in AS is strictly smaller than the smallest element of AU. The routine described here is a partition-based loop. In the course of the i th iteration, the length of AU is ni , with ni < ni−1. Initially, for i = 0, AS is empty, AU = A′, and n0 = n−2P < n. The loop proceeds as follows: First, find b � , an element of rank �ni/4� in AU. The selection procedure places this element at the right end of AU, so the configuration of A′ changes to ASA′Ub � . Here A′U denotes a mix of elements in AU, of length ni −1. Second, A′U is partitioned into two blocks A< and B≥ consisting, respectively, of elements strictly smaller than b � and of those greater than or equal to b � . The configuration of the array thus changes to AS A< B≥b � . The respective lengths of A< and B≥ will be denoted here by ni,< and ni,≥. Note that, even for a large block AU, we may obtain a very short block A<, since many elements may be equal to b � . In fact, the block A< may even be empty, of length ni,< = 0. Third, sort the block A< by the procedure described in Section 2, using some initial segments of �L and �R as a pointer memory and of B≥ as a buffer memory, with b � as a buffer separator. This is possible, since b � has been selected as an element of rank �ni/4�, and hence ni,< ≤ �ni/4�−1 ≤ ni/4, with ni,< +ni,≥ +1 = ni . But the required size of buffer is only 3ni,<−1 ≤ 3/4·ni−1 = ni−1−ni/4 ≤ ni−1−ni,< = ni,≥. Therefore, the block B≥ of length ni,≥ is sufficiently long. Similarly, the required 534 G. FRANCESCHINI AND V. GEFFERT number of bits for pointers is �4ni,</(log ni,<)2� ≤ �4(n/4)/(log(n/4))2� = P , and hence the pointer memory is also sufficiently large. (If ni,< ≤216, A< is sorted as a short block.) Fourth, restore the sorted order in �L and �R, by clearing all bits of the pointer memory to zero. Among others, this is required because the procedure of Section 2 will also be used in subsequent iterations, when it assumes that all bits are initially cleared. Fifth, after sorting A<, the configuration of A′ is AS A<,S B ′≥b � , where A<,S denotes the sorted version of the block A< and B ′≥ a mixed up version of B≥. Now put the first element in B ′≥ aside and move b � to the first position after A<,S. After that, collect all elements smaller than or equal to b � to the left part of B ′≥, processing also the element put aside. Since B≥ did not contain elements strictly smaller than b � , this actually partitions B ′≥ into two blocks A= and B> consisting, respectively, of elements equal to b � and of those strictly greater than b � , of respective lengths ni,= and ni,>. Clearly, ni,=+ni,> = ni,≥. The configuration has changed toAS A<,Sb�A= B>. Sixth, observe thatAS A<,Sb � A= and B> can be viewed as “new” variants of blocks AS and AU. Thus, we can start a new iteration, with B> as a new block AU, of length ni+1 = ni,>. The above process is iterated until the length of unsorted part drops to 216, or below. This residue is then sorted as a short block, without using a buffer or pointers, which will be described later, in Section 3.3. Now we can derive computational costs. First, recall that b � has been selected as an element of rank �ni/4�, and hence ni+1 = ni,> ≤ ni −�ni/4� ≤ 3/4 ·ni . Taking into account that n0 ≤n, we get ni ≤ (3/4)i · n, for each i ≥0. This gives that I−1∑ i=0 ni ≤ 4n , I ≤ O(log n) , (9) where I denotes the number of iterations. Second, it is easy to see that I−1∑ i=0 (ni,< + 1 + ni,=) + nI ≤ n , (10) since, in different iterations, the final locations occupied by A<,S, b � , and A=, do not overlap. Here nI denotes the length of the residual short block. Let us now present the costs for the i th iteration. Selection of b � , an element of the given rank in a block of length ni , costs O(ni ) comparisons and ε · ni moves, by Geffert and Kollár [2001]. Partitioning of A′U into blocks A< and B≥ can be done with ni comparisons and 2ni,< + 1 moves, since the length of A′U is ni − 1, and the number of collected elements, strictly smaller than b � , is ni,<. The cost of sorting the block A< is bounded by 2ni,< · log ni,< + O(ni,< · (log ni,<)4/5) ≤ 2ni,< · log n + O(ni,< · (log n)4/5) comparisons and (11 + ε) · ni,< moves, by Theorem 2.7. Sorting of the block A< is followed by restoring the sorted order in �L and �R, by clearing all bits, which costs O(P) ≤ O(n/(log n)2) comparisons, together with the same number of moves. Positioning b � to the right of A<,S requires only 2 element moves. Finally, the i th iteration is concluded by partitioning B ′≥ into blocks A= and B>, with at most ni,≥ ≤ ni comparisons and 2ni,= + 1 moves, since the length An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 535 of B ′≥ is ni,≥, and the number of collected elements, equal to b � , is ni,=. The cost of sorting the residual short block does not exceed the bounds for the standard case; 2nI · log nI + 6.25nI ≤ 2nI · log n + O(nI · (log n)4/5) comparisons and 9.75nI ≤ (11 + ε) · nI moves. (See Section 3.3 below.) Now we can sum the above costs over all iterations, using (9) and (10). For the number of comparisons, this gives C(n) ≤ I−1∑ i=0 ni · O(1) + I−1∑ i=0 ni,< · ( 2 log n + O((log n)4/5)) + I−1∑ i=0 O ( n/(log n)2 ) + nI · (2 log n + O((log n)4/5)) ≤ O(n) + ( I−1∑ i=0 (ni,< + 1 + ni,=) + nI ) · (2 log n + O((log n)4/5)) +O(n/ log n) ≤ O(n) + n · (2 log n + O((log n)4/5)) + O(n/ log n) ≤ 2n · log n + O(n · (log n)4/5) . For the number of moves, we get M(n) ≤ I−1∑ i=0 ε · ni + I−1∑ i=0 (13 + ε) · ni,< + I−1∑ i=0 2ni,= + I−1∑ i=0 O ( n/(log n)2 ) + (11 + ε) · nI ≤ ε · n + ( I−1∑ i=0 (ni,< + 1 + ni,=) + nI ) · (13 + ε) + O(n/ log n) ≤ ε · n + n · (13 + ε) + O(n/ log n) ≤ (13 + ε) · n , where ε > 0 is an arbitrarily small, but fixed, real constant. The above analysis did not include the costs of the initial building of pointer memory. However, by Lemma 3.1, this can be done with only O(n) comparisons and O(n/ log n) moves, and hence the bounds displayed above represent the total computational costs of the algorithm. THEOREM 3.2. The given array, consisting of n elements, can be sorted in- place by performing at most 2n · log n + o(n · log n) comparisons and (13 + ε) · n element moves, where ε > 0 denotes an arbitrarily small, but fixed, real constant. The number of auxiliary arithmetic operations with indices is bounded by O(n · log n). 3.3. HANDLING SHORT BLOCKS. The algorithm presented above needs a pro- cedure capable of sorting blocks of small lengths, namely, with m ≤216 = 65536. This is required, among others, to sort blocks A< that are short. We could sweep the problem under the rug by saying that “short” blocks can, “somehow,” be sorted with O(1) comparisons and moves, since they are of constant lengths. However, the upper bounds presented by Theorem 2.7 in Section 2.12 require some more details, 536 G. FRANCESCHINI AND V. GEFFERT especially for (11 + ε) · m, the number of moves. Last but not least, these lengths are important in practice. One of the possible simple solutions is to use our version of heapsort, with 5 roots and internal nodes having 5 sons. Using the analysis presented in Section 3.1, devoted to building a pointer memory, for t = 5, m ≤ 216, and hence for at most q ≤ 1 + �logt m� ≤ 7 levels, one can easily verify that we shall never use more than 2m · log m +6.25m comparisons or 9.75m moves. (These bounds are not tight, we leave further improvement to the reader.) 3.4. AN ALTERNATIVE SOLUTION. As pointed out at the end of Section 2.8, devoted to extracting sorted elements from segments, we could use a heap structure with four levels, instead of five, in a segment. This slightly reduces the number of moves, but increases the number of comparisons. The detailed argument parallels the proof of Theorem 3.2, and hence it is left to the reader. COROLLARY 3.3. The given array, consisting of n elements, can be sorted in- place by performing at most 6n · log n + o(n · log n) comparisons and (12 + ε) · n element moves, where ε>0 denotes an arbitrarily small, but fixed, real constant. 4. Concluding Remarks We have described the first in-place sorting algorithm performing O(n · log n) comparisons and O(n) element moves in the worst case, which closes a long- standing open problem. However, the algorithms presented in Theorem 3.2 and Corollary 3.3 do not sort stably, since the order of buffer elements may change. If some elements used in buffers are equal, their original order cannot be recovered. This leaves us with a fascinating question: Does there exist an algorithm operating in-place and performing, in the worst case, at most O(n · log n) comparisons, O(n) moves, O(n · log n) arithmetic operations, and, at the same time, sorting elements stably, so that the relative order of equal elements is preserved? At the present time, we dare not formulate any conjectures about this problem. The best known algorithm for stable in-place sorting with O(n) moves is still the one presented in Munro and Raman [1996a], performing O(n1+ε) comparisons in the worst case. We are also firmly convinced that the upper bounds of Theorem 3.2 and Corol- lary 3.3 are not optimal and can be improved, which is left as another open problem. REFERENCES CARLSSON, S. 1992. A note on Heapsort. Comput. J. 35, 410–411. FLOYD, R. 1964. Treesort 3 (Algorithm 245). Commun. ACM 7, 701. FRANCESCHINI, G. 2003. An in-place sorting algorithm performing O(n log n) comparisons and O(n) data moves. Tech. rep., Dipartimento di Informatica, Università di Pisa. March. (Available from ftp://ftp.di.unipi.it/pub/techreports/TR-03-06.ps.Z.) GEFFERT, V. 2002. Sorting with O(n log n) comparisons and O(n) transports, in-place, in the worst case, simultaneously. Tech. rep., P. J. Šafárik University. July. (Available from http://ics.upjs.sk/techreports/ 2002/ultim.ps.) GEFFERT, V., AND KOLLÁR, J. 2001. Linear-time in-place selection in ε · n element moves. Tech. rep., P. J. Šafárik University. April. (Available from http://ics.upjs.sk/techreports/2002/select.ps.) An In-Place Sorting with O(n log n) Comparisons and O(n) Moves 537 ITAI, A., KONHEIM, A., AND RODEH, M. 1981. A sparse table implementation of priority queues. In Proceedings of the International Colloquium on Automata, Languages, & Programming. Lecture Notes in Computer Science, vol. 115. Springer-Verlag, New York, 417–431. KATAJAINEN, J., AND PASANEN, T. 1999. In-place sorting with fewer moves. Inf. Process. Lett. 70, 31–37. KATAJAINEN, J., PASANEN, T., AND TEUHOLA, J. 1996. Practical in-place Mergesort. Nord. J. Comput. 3, 27–40. KNUTH, D. 1973. The Art of Computer Programming, Vol. 3: Sorting and Searching. Addison-Wesley, Reading, Mass. (Second edition: 1998). LI, M., AND VITÁNYI, P. 1993. An Introduction to Kolmogorov Complexity and Its Applications. Springer- Verlag, Section 6.3.1: Heapsort, 334–338. MUNRO, J., AND RAMAN, V. 1992. Sorting with minimum data movement. J. Algorithms 13, 374–393. MUNRO, J., AND RAMAN, V. 1996a. Fast stable in-place sorting with O(n) data moves. Algorithmica 16, 151–160. MUNRO, J., AND RAMAN, V. 1996b. Selection from read-only memory and sorting with minimum data movement. Theoret. Comput. Sci. 165, 311–323. RAMAN, V. 1991. Sorting in-place with minimum data movement. Ph.D. dissertation, Univ. Waterloo, Dept. Comput. Sci. (Tech. Rep. 91-12). REINHARDT, K. 1992. Sorting in-place with a worst case complexity of n log n − 1.3n + O(log n) comparisons and ε n log n + O(1) transports. In Proceedings of the International Symposium on the Algorithms and Computers. Lecture Notes in Computer Science, Vol. 650. Springer-Verlag, New York, 489–498. SCHAFFER, R., AND SEDGEWICK, R. 1993. The analysis of Heapsort. J. Algorithms 15, 76–100. WEGENER, I. 1993. Bottom-Up-Heapsort, A new variant of Heapsort beating, on an average, Quicksort (if n is not very small). Theoret. Comput. Sci. 118, 81–98. WILLARD, D. 1982. Maintaining dense sequential files in a dynamic environment. In Proceedings of the Symposium on the Theory of Computing. ACM, New York, 114–121. WILLIAMS, J. 1964. Heapsort (Algorithm 232). Commun. ACM 7, 347–348. RECEIVED MAY 2003; REVISED JUNE 2005; ACCEPTED MAY 2005 Journal of the ACM, Vol. 52, No. 4, July 2005.

## Related Questions

Tutlance Experts offer help in a wide range of topics. Here are some of our top services:

- Math homework help
- Nursing homework help
- Statistics homework help
- Nursing coursework help
- Capstone project writing services
- Essay writers for hire
- Case study writing help
- Buy college papers online
- Buy college research papers
- College homework help
- Professional resume writing services
- Programming homework help
- Coursework writing help
- Term paper writing help
- Biology homework help
- Do my physics homework
- Dissertation data analysis help
- PhD Dissertation writing services
- Chemistry homework help

Post your project now for free and watch professional experts outbid each other in just a few minutes.