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.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.3.tar.gz (41.0 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.3-cp314-cp314-macosx_10_15_universal2.whl (93.6 kB view details)

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

orderedstructs-0.4.3-cp313-cp313-macosx_10_13_universal2.whl (93.2 kB view details)

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

orderedstructs-0.4.3-cp312-cp312-macosx_10_13_universal2.whl (93.3 kB view details)

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

orderedstructs-0.4.3-cp311-cp311-macosx_10_9_universal2.whl (93.8 kB view details)

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

orderedstructs-0.4.3-cp310-cp310-macosx_10_9_universal2.whl (93.8 kB view details)

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

File details

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

File metadata

  • Download URL: orderedstructs-0.4.3.tar.gz
  • Upload date:
  • Size: 41.0 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.3.tar.gz
Algorithm Hash digest
SHA256 06066828de3e80ac36fe40f6c987bc691337ad4c4a6f9dee6b46b2ebb2c6732e
MD5 19aa917b7e3bf812d336c212e65dbdd5
BLAKE2b-256 ac509449c75296d69d6a607de5b967082b5b1baa77a5c3482ea5c91472d4ffae

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for orderedstructs-0.4.3-cp314-cp314-macosx_10_15_universal2.whl
Algorithm Hash digest
SHA256 4782ae7dc66a7c146edbcd6ee5357f90071e7398599bbf2a71677a5149778efc
MD5 3f3e617cf5add1f3a811f2d626b3f756
BLAKE2b-256 55a297cceee7a695f887dae905ad7077ac586cd66a57bd72e341de9757fac64f

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for orderedstructs-0.4.3-cp313-cp313-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 d951baac509103bb75450f55d831cf75abb494a04f913c5001b3b9f5766e1611
MD5 d8ca2497f932cccf12f99b448408265d
BLAKE2b-256 3723b3f7ffa5cd359e7dfed1b1c8b7bb05cf2303a44acb5957045a9a98255fac

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for orderedstructs-0.4.3-cp312-cp312-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 6414d13aac6bce9a21c183c78dc3ae19f982c7f50a85f385b1d8fd926e36b1ec
MD5 b882d2e75013908553e683f8c3a9a1f5
BLAKE2b-256 4b54467b75ac3bf031b21fabd73da1f1d4a9710558bda4b3133a899499435999

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for orderedstructs-0.4.3-cp311-cp311-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 c1216440efa8a68f51e4b1b006f2ff2d9bfc0ae06cc566ce28abd069d1040ed6
MD5 368138bf8ff8ee958a9e16a9a431ed50
BLAKE2b-256 cd31ace2e6a1a787b17734b58ca10280aa59240d9ce9b014a81ad959ea5ba8b4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for orderedstructs-0.4.3-cp310-cp310-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 d1d0c026fccc927e7b7cef2d8d84bc6f4745b040f638fcc9c234ea1d8b4b92d7
MD5 fcf859933bc11789635c1a602f79e80a
BLAKE2b-256 6107051b82137a99b896739b927326c1fd5c538ae1e788c6742b073df2774266

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