Skip Lists

This is an implementation of William Pugh's skip lists as an associative container with unique keys. At the lowest level, it is a doubly linked list to allow bidirectional iterators, but at higher levels it is only singly linked as they are used for searching only.

The operator << has been overloaded to print the skip lists as is shown here:

Size:     75
Height:   6
Total:    1008
Average:  13.44
5-----------------------------------------------------------|-----------------> 0
4------------------|----------------------------------------|----|------------> 0
3------------|-----|----------------------------------------|----|-|---|------> 0
2-------|---||--|--|-|-------------|--|-----------|-|-------|----|-|---|------> 0
1-----|-|--|||--||-||||-------||--||--|--|---|-||||-|---|---|----|||-|-|-|--|-> 0
0-***************************************************************************-> 0

Size:     75
Height:   5
Total:    965
Average:  12.8667
4-----------------------------------------|-----------------------------------> 0
3----||------------|----------------------|---------------------------------|-> 0
2----||-|--||----|-|-----|-----|----|--|--|----------------|---||-----------|-> 0
1---|||-||-||----|-||----|---|-|--|-|--|--||-|------|-|--|-|-|-||---||--|---|-> 0
0-***************************************************************************-> 0

These are two randomly generated skip lists created by the source test.cpp. The values total and average indicate the total and average number of comparisons necessary to find entries in the skip list. Surprisingly, it is very economical: on the same order as a binary search and not significantly worse, as is shown in Figure 1.

Figure 1. Average number of comparisons for successful searches in a binary search (blue) and the best-fitting curve using skip lists.

The output of a larger example with approximately 1000 nodes is shown here.

Here is the class description:

Skip List
class Skip_list<K, Type>

Accessors

bool empty() const

Returns true if the skip list is empty. (Θ(1))

int size() const

The size function returns the number of objects stored within the skip list. (Θ(1))

int count( K const &key ) const

The count function returns the number of instances of key—either 0 or 1. (Θ(ln(n))

Type operator[]( K const &key ) const

Returns a copy of the entry associated with key returning Type() if the key is not stored in the skip list. (O(ln(n)))

int total_search() const
double average_search() const
bool validate() const

These three functions are used to investigate the structure and run times. The first returns the total number of nodes which must be inspected to required each of the keys in the skip list; the second divides this result by the number of objects in the skip list; and the third ensures that the skip list structure is internally consistent. (O(n ln(n)))

Mutators

Type &operator[]( K const &key )

Returns a reference to the entry associated with key within the skip list. (O(ln(n)))

void clear() const

The clear function removes all objects from the skip list. (Θ(n))

Iterator Functions

iterator begin()

Returns an iterator referring to the first entry in the skip list. (Θ(1))

iterator end()

Returns an iterator referring to one-past-the-last entry in the skip list. (Θ(1))

iterator find( K const &key )

If key is in the skip list, it returns an iterator which references that entry; otherwise, it returns end(). (O(ln(n)))

Pair
class Skip_list<K, Type>::pair

The class pair has two public member variables: first, a constant reference to the key; and second, a reference to the associated value.

Iterator
class Skip_list<K, Type>::iterator

The iterator class stores a reference to a node within the skip list and access to ordered pairs of (key, value).

iterator &operator++()
++itr

Set the reference to the next node (if any) and return a reference to this iterator. (Θ(1))

iterator &operator++( int )
itr++

Set the reference to the next node (if any) but return an iterator referring to the original node. (Θ(1))

iterator &operator--()
--itr

Set the reference to the previous node (if any) and return a reference to this iterator. (Θ(1))

iterator &operator--( int )
itr--

Set the reference to the previous node (if any) but return an iterator referring to the original node. (Θ(1))

iterator &operator*()
*itr

Return a pair consisting of a constant reference to the key and a reference to the value of the referred node. (Θ(1))

bool operator==( iterator const &rhs )

Return true if the two iterators are referring to the same node and false otherwise. (Θ(1))

bool operator!=( iterator const &rhs )

Return true if the two iterators are referring to different nodes and false otherwise. (Θ(1))