The XFastTrie is a vast--even exponential--improvement over the BinaryTrie in terms of query time, but the and operations are still not terribly fast. Furthermore, the space usage, , is higher than the other SSet implementations described in this book, which all use space. These two problems are related; if operations build a structure of size , then the operation requires at least on the order of time (and space) per operation.
The YFastTrie, discussed next, simultaneously improves the space and speed of XFastTries. A YFastTrie uses an XFastTrie, , but only stores values in . In this way, the total space used by is only . Furthermore, only one out of every or operations in the YFastTrie results in an or operation in . By doing this, the average cost incurred by calls to 's and operations is only constant.
The obvious question becomes: If only stores / elements, where do the remaining elements go? These elements move into secondary structures, in this case an extended version of treaps (Section 7.2). There are roughly / of these secondary structures so, on average, each of them stores items. Treaps support logarithmic time SSet operations, so the operations on these treaps will run in time, as required.
More concretely, a YFastTrie contains an XFastTrie, , that contains a random sample of the data, where each element appears in the sample independently with probability . For convenience, the value , is always contained in . Let denote the elements stored in . Associated with each element, , is a treap, , that stores all values in the range . This is illustrated in Figure 13.7.
The
operation in a YFastTrie is fairly easy. We search
for
in
and find some value
associated with the treap
. We then use the treap
method on
to answer
the query. The entire method is a one-liner:
The first
operation (on
) takes
time.
The second
operation (on a treap) takes time, where
is the size of the treap. Later in this section, we will show that
the expected size of the treap is
so that this operation takes
time.13.1
Adding an element to a YFastTrie is also fairly simple--most of the time. The method calls to locate the treap, , into which should be inserted. It then calls to add to . At this point, it tosses a biased coin that comes up as heads with probability and as tails with probability . If this coin comes up heads, then will be added to .
This is where things get a little more complicated. When
is added to
, the treap
needs to be split into two treaps,
and
.
The treap
contains all the values less than or equal to
;
is the original treap,
, with the elements of
removed.
Once this is done, we add the pair
to
. Figure 13.8
shows an example.
|
The
method undoes the work performed by
.
We use
to find the leaf,
, in
that contains the answer
to
. From
, we get the treap,
, containing
and remove
from
. If
was also stored in
(and
is not equal to
) then we remove
from
and add the
elements from
's treap to the treap,
, that is stored by
's
successor in the linked list. This is illustrated in
Figure 13.9.
Earlier in the discussion, we delayed arguing about the sizes of treaps in this structure until later. Before finishing this chapter, we prove the result we need.
Similarly, the elements of smaller than are where all these coin tosses turn up as tails and the coin toss for turns up as heads. Therefore, , since this is the same coin tossing experiment considered in the preceding paragraph, but one in which the last toss is not counted. In summary, , so
|
Lemma 13.1 was the last piece in the proof of the following theorem, which summarizes the performance of the YFastTrie:
The term in the space requirement comes from the fact that always stores the value . The implementation could be modified (at the expense of adding some extra cases to the code) so that it is unnecessary to store this value. In this case, the space requirement in the theorem becomes .