Skip Lists: A Probabilistic Alternative to
Balanced Trees
Skip lists are a data structure that can be used in place of balanced trees.
Skip lists use probabilistic balancing rather than strictly enforced balancing
and as a result the algorithms for insertion and deletion in skip lists are
much simpler and significantly faster than equivalent algorithms for
balanced trees.
William Pugh
Binary trees can be used for representing abstract data types
such as dictionaries and ordered lists. They work well when
the elements are inserted in a random order. Some sequences
of operations, such as inserting the elements in order, produce
degenerate data structures that give very poor performance. If
it were possible to randomly permute the list of items to be in-
serted, trees would work well with high probability for any in-
put sequence. In most cases queries must be answered on-line,
so randomly permuting the input is impractical. Balanced tree
algorithms re-arrange the tree as operations are performed to
maintain certain balance conditions and assure good perfor-
mance.
Skip lists are a probabilistic alternative to balanced trees.
Skip lists are balanced by consulting a random number gen-
erator. Although skip lists have bad worst-case performance,
no input sequence consistently produces the worst-case per-
formance (much like quicksort when the pivot element is cho-
sen randomly). It is very unlikely a skip list data structure will
be significantly unbalanced (e.g., for a dictionary of more
than 250 elements, the chance that a search will take more
than 3 times the expected time is less than one in a million).
Skip lists have balance properties similar to that of search
trees built by random insertions, yet do not require insertions
to be random.
Balancing a data structure probabilistically is easier than
explicitly maintaining the balance. For many applications,
skip lists are a more natural representation than trees, also
leading to simpler algorithms. The simplicity of skip list algo-
rithms makes them easier to implement and provides signifi-
cant constant factor speed improvements over balanced tree
and self-adjusting tree algorithms. Skip lists are also very
space efficient. They can easily be configured to require an
average of 1
1
/
3
pointers per element (or even less) and do not
require balance or priority information to be stored with each
node.
SKIP LISTS
We might need to examine every node of the list when search-
ing a linked list (Figure 1a). If the list is stored in sorted order
and every other node of the list also has a pointer to the node
two ahead it in the list (Figure 1b), we have to examine no
more than n/2 + 1 nodes (where n is the length of the list).
Also giving every fourth node a pointer four ahead (Figure
1c) requires that no more than n/4 + 2 nodes be examined.
If every (2
i
)
th
node has a pointer 2
i
nodes ahead (Figure 1d),
the number of nodes that must be examined can be reduced to
log
2
n while only doubling the number of pointers. This
data structure could be used for fast searching, but insertion
and deletion would be impractical.
A node that has k forward pointers is called a level k node.
If every (2
i
)
th
node has a pointer 2
i
nodes ahead, then levels
of nodes are distributed in a simple pattern: 50% are level 1,
25% are level 2, 12.5% are level 3 and so on. What would
happen if the levels of nodes were chosen randomly, but in the
same proportions (e.g., as in Figure 1e)? A node’s i
th
forward
pointer, instead of pointing 2
i–1
nodes ahead, points to the
next node of level i or higher. Insertions or deletions would
require only local modifications; the level of a node, chosen
randomly when the node is inserted, need never change. Some
arrangements of levels would give poor execution times, but
we will see that such arrangements are rare. Because these
data structures are linked lists with extra pointers that skip
over intermediate nodes, I named them skip lists.
SKIP LIST ALGORITHMS
This section gives algorithms to search for, insert and delete
elements in a dictionary or symbol table. The Search opera-
tion returns the contents of the value associated with the de-
sired key or failure if the key is not present. The Insert opera-
tion associates a specified key with a new value (inserting the
key if it had not already been present). The Delete operation
deletes the specified key. It is easy to support additional oper-
ations such as “find the minimum key” or “find the next key”.
Each element is represented by a node, the level of which
is chosen randomly when the node is inserted without regard
for the number of elements in the data structure. A level i
node has i forward pointers, indexed 1 through i. We do not
need to store the level of a node in the node. Levels are
capped at some appropriate constant MaxLevel. The level of a
list is the maximum level currently in the list (or 1 if the list is
empty). The header of a list has forward pointers at levels one
through MaxLevel. The forward pointers of the header at
levels higher than the current maximum level of the list point
to NIL.