Skip to main content

Contains a variety of ordered structures, in particular a SkipList.

Project description

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.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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

orderedstructs-0.4.4.tar.gz (65.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

orderedstructs-0.4.4-cp314-cp314-macosx_10_15_universal2.whl (93.7 kB view details)

Uploaded CPython 3.14macOS 10.15+ universal2 (ARM64, x86-64)

orderedstructs-0.4.4-cp313-cp313-macosx_10_13_universal2.whl (93.4 kB view details)

Uploaded CPython 3.13macOS 10.13+ universal2 (ARM64, x86-64)

orderedstructs-0.4.4-cp312-cp312-macosx_10_13_universal2.whl (93.4 kB view details)

Uploaded CPython 3.12macOS 10.13+ universal2 (ARM64, x86-64)

orderedstructs-0.4.4-cp311-cp311-macosx_10_9_universal2.whl (93.9 kB view details)

Uploaded CPython 3.11macOS 10.9+ universal2 (ARM64, x86-64)

orderedstructs-0.4.4-cp310-cp310-macosx_10_9_universal2.whl (93.9 kB view details)

Uploaded CPython 3.10macOS 10.9+ universal2 (ARM64, x86-64)

File details

Details for the file orderedstructs-0.4.4.tar.gz.

File metadata

  • Download URL: orderedstructs-0.4.4.tar.gz
  • Upload date:
  • Size: 65.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.10.11

File hashes

Hashes for orderedstructs-0.4.4.tar.gz
Algorithm Hash digest
SHA256 3f35aaa6afdb4ce480919b3f4fbd92079d5ae35aceaab4c1d2357c43cea13cbd
MD5 c59b49aa0c672910a27cb9349dc8b13f
BLAKE2b-256 59066a4bcbc8c4d218d6a6cab9633fccd31a47e553b534c77de2fe4281085729

See more details on using hashes here.

File details

Details for the file orderedstructs-0.4.4-cp314-cp314-macosx_10_15_universal2.whl.

File metadata

File hashes

Hashes for orderedstructs-0.4.4-cp314-cp314-macosx_10_15_universal2.whl
Algorithm Hash digest
SHA256 10201dc57670dd885c9a45f8b452fa1829d9bae24b7d831e4db382ef39e75ac6
MD5 b63aa917544653fe227c8af5fc4ac70d
BLAKE2b-256 f03dee12e90370798d3307d8d2ac9cb5f32656a559a50c9bf50220c3014f1eff

See more details on using hashes here.

File details

Details for the file orderedstructs-0.4.4-cp313-cp313-macosx_10_13_universal2.whl.

File metadata

File hashes

Hashes for orderedstructs-0.4.4-cp313-cp313-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 c322241e3c3fec7e08246176006cfd79cfbf8243e84d1fcbda55cd97f0d1aa69
MD5 722e9a1214677c24a4fe4b0cf3efde3b
BLAKE2b-256 afe1c811fdd4ba6f2c561cd359ad631ccc8932de92304fd4ecbba7ea75b8e961

See more details on using hashes here.

File details

Details for the file orderedstructs-0.4.4-cp312-cp312-macosx_10_13_universal2.whl.

File metadata

File hashes

Hashes for orderedstructs-0.4.4-cp312-cp312-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 dc89b3642770583b4af7f60eb0d4fb4058cf3802997783da96cf42eed5e1b1fe
MD5 364afd049b42baca387440e0b502a844
BLAKE2b-256 4d248ecb0cb328d0ded0e37cbfc35017ec9e94224d5055b8f3bda07979949ba5

See more details on using hashes here.

File details

Details for the file orderedstructs-0.4.4-cp311-cp311-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for orderedstructs-0.4.4-cp311-cp311-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 1ba15f321ccfc9434c43affe218c8fbc962728c6d8828c11cb87a41fc8345d89
MD5 16bfb73d622afb22c4a77175f23b3b36
BLAKE2b-256 ac4c364d8d14ba2a5b26d26d08ba19cf5ecee62f21a483a1b32e693442363a9b

See more details on using hashes here.

File details

Details for the file orderedstructs-0.4.4-cp310-cp310-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for orderedstructs-0.4.4-cp310-cp310-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 98125712b279e5b2c6b2406c5ee082004c52b29bec9a108e43313eedc7761927
MD5 7de67d5de2c61f4baa7c3cc6cb567b13
BLAKE2b-256 7419ba4505c9362cc152b16e8054e5de9ca63649a0a642707caeb499ebda3f59

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page