Skip to main content

SkipList

This project contains a SkipList implementation in C++ with Python bindings.

A SkipList behaves as an always sorted list with, typically, O(log(n)) cost for insertion, look-up and removal. This makes it ideal for such operations as computing the rolling median of a large dataset.

See the full documentation on this project at ReadTheDocs.

A SkipList is implemented as a singly linked list of ordered nodes where each node participates in a subset of, sparser, linked lists. These additional 'sparse' linked lists provide rapid indexing and mutation of the underlying linked list. It is a probabilistic data structure using a random function to determine how many 'sparse' linked lists any particular node participates in. As such SkipList is an alternative to binary tree, Wikipedia has a introductory page on SkipLists.

An advantage claimed for SkipLists are that the insert and remove logic is simpler (however I do not subscribe to this). The drawbacks of a SkipList include its larger space requirements and its O(log(N)) lookup behaviour compared to other, more restricted and specialised, data structures that may have either faster runtime behaviour or lower space requirements or both.

This project contains a SkipList implementation in C++ with Python bindings with:

  • No capacity restrictions apart from available memory.
  • Works with any C++ type that has meaningful comparison operators.
  • The C++ SkipList can be compiled as thread safe.
  • The Python SkipList is thread safe.
  • The SkipList has exhaustive internal integrity checks.
  • Python SkipLists can be long/float/bytes/object types, the latter can have user defined comparison functions.
  • With Python 3.8+ SkipLists can be combined with the multiprocessing.shared_memory module for concurrent operation on large arrays. For example concurrent rolling medians which speed up near linearly with the number of cores.
  • The implementation is extensively performance tested in C++ and Python.

There are a some novel features to this implementation:

Credits

Originally written by Paul Ross with credits to: Wilfred Hughes (AHL), Luke Sewell (AHL) and Terry Tsantagoeds (AHL).

Installation

C++

This SkipList requires:

  • A C++20 compiler.
  • -I<skiplist>/src/cpp as an include path.
  • <skiplist>/src/cpp/SkipList.cpp to be compiled/linked.
  • The macro SKIPLIST_THREAD_SUPPORT set if you want a thread safe SkipList using C++ mutexes.

Python

This SkipList version supports Python 3.10, 3.11, 3.12, 3.13, 3.14 (and, probably, some earlier Python 3 versions).

From PyPi

$ pip install orderedstructs

From source

$ git clone https://github.com/paulross/skiplist.git
$ cd <skiplist>
$ python setup.py install

Testing

This SkipList has extensive tests for correctness and performance.

C++

To run all the C++ functional and performance tests:

$ cd <skiplist>/src/cpp
$ make release
$ ./SkipList_R.exe

To run the C++ functional tests with agressive internal integrity checks:

$ cd <skiplist>/src/cpp
$ make debug
$ ./SkipList_D.exe

To run all the C++ functional and performance tests for a thread safe SkipList:

$ cd <skiplist>/src/cpp
$ make release CXXFLAGS=-DSKIPLIST_THREAD_SUPPORT
$ ./SkipList_R.exe

Python

Testing requires pytest and hypothesis:

To run all the C++ functional and performance tests:

$ cd <skiplist>
$ py.test -vs tests/

Examples

Here are some examples of using a SkipList in your code:

C++

#include "SkipList.h"
    
// Declare with any type that has sane comparison.
OrderedStructs::SkipList::HeadNode<double> sl;

sl.insert(42.0);
sl.insert(21.0);
sl.insert(84.0);

sl.has(42.0) // true
sl.size()    // 3
sl.at(1)     // 42.0, throws OrderedStructs::SkipList::IndexError if index out of range

sl.remove(21.0); // throws OrderedStructs::SkipList::ValueError if value not present

sl.size()    // 2
sl.at(1)     // 84.0

The C++ SkipList is thread safe when compiled with the macro SKIPLIST_THREAD_SUPPORT, then a SkipList can then be shared across threads:

#include <thread>
#include <vector>

#include "SkipList.h"

void do_something(OrderedStructs::SkipList::HeadNode<double> *pSkipList) {
    // Insert/remove items into *pSkipList
    // Read items inserted by other threads.
}

OrderedStructs::SkipList::HeadNode<double> sl;
std::vector<std::thread> threads;

for (size_t i = 0; i < thread_count; ++i) {
    threads.push_back(std::thread(do_something, &sl));
}
for (auto &t: threads) {
    t.join();
}
// The SkipList now contains the totality of the thread actions.

Python

An example of using a SkipList of always ordered floats:

import orderedstructs

# Declare with a type. Supported types are long/float/bytes/object.
sl = orderedstructs.SkipList(float)

sl.insert(42.0)
sl.insert(21.0)
sl.insert(84.0)

sl.has(42.0) # True
sl.size()    # 3
sl.at(1)     # 42.0

sl.has(42.0) # True
sl.size()    # 3
sl.at(1)     # 42.0, raises IndexError if index out of range

sl.remove(21.0); # raises ValueError if value not present

sl.size()    # 2
sl.at(1)     # 84.0

The Python SkipList can be used with user defined objects with a user defined sort order. In this example the last name of the person takes precedence over the first name:

import functools

@functools.total_ordering
class Person:
    """Simple example of ordering based on last name/first name."""
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __eq__(self, other):
        try:
            return self.last_name == other.last_name and self.first_name == other.first_name
        except AttributeError:
            return NotImplemented

    def __lt__(self, other):
        try:
            return self.last_name < other.last_name or self.first_name < other.first_name
        except AttributeError:
            return NotImplemented

    def __str__(self):
        return '{}, {}'.format(self.last_name, self.first_name)

import orderedstructs

sl = orderedstructs.SkipList(object)

sl.insert(Person('Peter', 'Pan'))
sl.insert(Person('Alan', 'Pan'))
assert sl.size() == 2
assert str(sl.at(0)) == 'Pan, Alan' 
assert str(sl.at(1)) == 'Pan, Peter' 

The Python SkipList is thread safe when using any acceptable Python type even if that type has user defined comparison methods. This uses Pythons mutex machinery which is independent of C++ mutexes.

History

0.4.5 (2026-04-20)

  • Performance of low level shared memory operations.
  • Add support for Python 3.15
  • Support for Python 3.10, 3.11, 3.12, 3.13, 3.14, 3.15.
  • Development Status :: 5 - Production/Stable

0.4.4 (2026-02-15)

  • Demonstrate reverse ordering with Python objects.
  • Add further performance analysis of rolling median with multiprocessing shared memory.
  • Identify an issue with mixing pure Python code and C code in the orderedstructs package. Fixing this is known but would mean an API change.

0.4.3 (2026-02-09)

  • Add minor gridlines to log plots.
  • Fix git corruption.

0.4.0 (2026-01-31)

  • Support for Python 3.10, 3.11, 3.12, 3.13, 3.14.
  • End support for Python 3.8, 3.9, but they will work just fine.
  • Improve the performance tests and documentation.
  • Python performance benchmarks are now automatically converted into Gnuplot .dat files.
  • Improve C++ rolling median code.
  • Reorganise C++ header files to be more conventional which also gives Doxygen a better grasp of the code.
  • Development Status :: 5 - Production/Stable

0.3.17 (2026-01-21)

  • Support for Python 3.8, 3.9, 3.10, 3.11, 3.12, 3.13, 3.14.
  • Development Status :: 5 - Production/Stable

0.3.16 (2025-04-12)

  • Improvements to performance documentation.
  • Minor fix for a linker issue.
  • Support for Python 3.8, 3.9, 3.10, 3.11, 3.12, 3.13.
  • Development Status :: 5 - Production/Stable

0.3.15 (2025-02-12)

0.3.14 (2024-08-02)

  • Support for Python 3.8, 3.9, 3.10, 3.11, 3.12, 3.13.
  • Drop support for Python 3.7.

0.3.13 (2023-09-05)

  • Documentation improvements.

0.3.12 (2023-08-29)

  • Minor fixes to the documentation and examples.

0.3.11 (2023-08-28)

  • Minor fixes missed in 0.3.10 release.

0.3.10 (2023-08-28)

  • Minor fixes missed in 0.3.9 release.

0.3.9 (2023-08-28)

  • Minor fixes missed in 0.3.8 release.

0.3.8 (2023-08-28)

  • Add cmake build.
  • Support for Python 3.7, 3.8, 3.9, 3.10, 3.11.
  • Remove mention of Python 2.

0.3.7 (2021-12-18)

  • Fix build on GCC (Issue #8).

0.3.6 (2021-12-18)

  • Add documentation on NaN in rolling median.
  • Add plots using shared_memory.
  • Add Python 3.10 support.

0.3.5 (2021-05-02)

  • Fix uncaught exception when trying to remove a NaN.

0.3.4 (2021-04-28)

  • Improve documentation mainly around multiprocessing.shared_memory and tests.

0.3.3 (2021-03-25)

  • Add Python benchmarks, fix some build issues.

0.3.2 (2021-03-18)

  • Fix lambda issues with Python 3.8, 3.9.

0.3.1 (2021-03-17)

  • Support Python 3.7, 3.8, 3.9.

0.3.0 (2017-08-18)

  • Public release.
  • Allows storing of PyObject* and rich comparison.

0.2.0

Python module now named orderedstructs.

0.1.0

Initial release.

Metadata

Release files for orderedstructs 0.4.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for orderedstructs 0.4.5
File Size Uploaded
orderedstructs-0.4.5.tar.gz 66.5 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for orderedstructs 0.4.5
File
orderedstructs-0.4.5-cp315-cp315-macosx_10_15_universal2.whl CPython 3.15 CPython 3.15 macOS 10.15+ universal2 (ARM64, x86-64) Details
orderedstructs-0.4.5-cp314-cp314-macosx_10_15_universal2.whl CPython 3.14 CPython 3.14 macOS 10.15+ universal2 (ARM64, x86-64) Details
orderedstructs-0.4.5-cp313-cp313-macosx_10_13_universal2.whl CPython 3.13 CPython 3.13 macOS 10.13+ universal2 (ARM64, x86-64) Details
orderedstructs-0.4.5-cp312-cp312-macosx_10_13_universal2.whl CPython 3.12 CPython 3.12 macOS 10.13+ universal2 (ARM64, x86-64) Details
orderedstructs-0.4.5-cp311-cp311-macosx_10_9_universal2.whl CPython 3.11 CPython 3.11 macOS 10.9+ universal2 (ARM64, x86-64) Details
orderedstructs-0.4.5-cp310-cp310-macosx_10_9_universal2.whl CPython 3.10 CPython 3.10 macOS 10.9+ universal2 (ARM64, x86-64) Details

Total release size: 628.8 kB

Release files / orderedstructs-0.4.5.tar.gz

Download URL orderedstructs-0.4.5.tar.gz
Size 66.5 kB
Tags Source
SHA-256 checksum
How to use checksums
34a85215b59c2a29578600f19ccb4ed1f945f9a60ba20f965e4db7a5edb9a0c6
BLAKE2b-256 checksum
How to use checksums
b77d981335e6f091a93188c622382fe5f08189deb5b59388ed6809c06f494828
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release files / orderedstructs-0.4.5-cp315-cp315-macosx_10_15_universal2.whl

Download URL orderedstructs-0.4.5-cp315-cp315-macosx_10_15_universal2.whl
Size 93.8 kB
Tags CPython 3.15 macOS 10.15+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
18028d632dc6f83cf99d3376f5308fad912c0984ba221a57cc52a46c60c71ac2
BLAKE2b-256 checksum
How to use checksums
df754bee3e79d09fe0466f1df750d213973120edb999dc5af3d1edf78f72fc30
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release files / orderedstructs-0.4.5-cp314-cp314-macosx_10_15_universal2.whl

Download URL orderedstructs-0.4.5-cp314-cp314-macosx_10_15_universal2.whl
Size 93.8 kB
Tags CPython 3.14 macOS 10.15+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
3454dc5e33f217fb747510c21b4254a4e706e3b91209fa09aa52d030d080c4a1
BLAKE2b-256 checksum
How to use checksums
2d8c08aed94fae8a6ddc13a239cf2b83bbf2ce71c4e1a8696f28756a7fe7e6cc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release files / orderedstructs-0.4.5-cp313-cp313-macosx_10_13_universal2.whl

Download URL orderedstructs-0.4.5-cp313-cp313-macosx_10_13_universal2.whl
Size 93.4 kB
Tags CPython 3.13 macOS 10.13+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
e4b1ca7c2ff609ca61125b130c68aca525d5774c52da2176d6ecb30e4d6e8834
BLAKE2b-256 checksum
How to use checksums
3606266cf7a18a080c8562d5b9b20566903294ec6a61639e003d0feb49be7716
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release files / orderedstructs-0.4.5-cp312-cp312-macosx_10_13_universal2.whl

Download URL orderedstructs-0.4.5-cp312-cp312-macosx_10_13_universal2.whl
Size 93.4 kB
Tags CPython 3.12 macOS 10.13+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
c1f83fd03e3587603e0e48fb64df8afe9da1ecf4076ac575d7830961456c4ab6
BLAKE2b-256 checksum
How to use checksums
00d275e422c8cfc019ce146c5efc6b59d01188626a8315c6c961c9bfd8f3d95e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release files / orderedstructs-0.4.5-cp311-cp311-macosx_10_9_universal2.whl

Download URL orderedstructs-0.4.5-cp311-cp311-macosx_10_9_universal2.whl
Size 94.0 kB
Tags CPython 3.11 macOS 10.9+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
e8537a51f14320bd761de8aa44f5e9144619abcfe03142ad107ba831e29834ad
BLAKE2b-256 checksum
How to use checksums
e6841aa21538e4cd3c52107b580ac53596f04fae9195f5f0969417fcf0614841
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release files / orderedstructs-0.4.5-cp310-cp310-macosx_10_9_universal2.whl

Download URL orderedstructs-0.4.5-cp310-cp310-macosx_10_9_universal2.whl
Size 94.0 kB
Tags CPython 3.10 macOS 10.9+ universal2 (ARM64, x86-64)
SHA-256 checksum
How to use checksums
72c102a7c9da347e8d86dbbd3da5826b835d23e4f977fdad8013148ab79681c4
BLAKE2b-256 checksum
How to use checksums
cce2eb0c2bd38e3c6921a0f3f810ef15ba9b933e62736b63089c2732c7b50dd5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.10.11

Release history Release notifications | RSS feed

This release

0.4.5 This release

7 release files

0.4.4

6 release files

0.4.3

6 release files

0.4.0

6 release files

0.3.17

8 release files

0.3.16

7 release files

0.3.15

7 release files

0.3.11

6 release files

0.3.9

6 release files

0.3.8

6 release files

0.3.7

6 release files

0.3.6

6 release files

0.3.5

5 release files

0.3.4

5 release files

0.3.3

5 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page