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.8, 3.9, 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.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.3.17.tar.gz (40.2 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.3.17-cp314-cp314-macosx_10_15_universal2.whl (93.4 kB view details)

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

orderedstructs-0.3.17-cp313-cp313-macosx_10_13_universal2.whl (93.1 kB view details)

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

orderedstructs-0.3.17-cp312-cp312-macosx_10_13_universal2.whl (93.1 kB view details)

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

orderedstructs-0.3.17-cp311-cp311-macosx_10_9_universal2.whl (93.6 kB view details)

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

orderedstructs-0.3.17-cp310-cp310-macosx_10_9_universal2.whl (93.6 kB view details)

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

orderedstructs-0.3.17-cp39-cp39-macosx_10_9_universal2.whl (93.6 kB view details)

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

orderedstructs-0.3.17-cp38-cp38-macosx_11_0_universal2.whl (93.2 kB view details)

Uploaded CPython 3.8macOS 11.0+ universal2 (ARM64, x86-64)

File details

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

File metadata

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

File hashes

Hashes for orderedstructs-0.3.17.tar.gz
Algorithm Hash digest
SHA256 e49900166815d6b566a932e283907bab2543d0a2eba62fc294ae6934de3658bf
MD5 c31231560e101d9dd66aa2eaa9389a9a
BLAKE2b-256 6a2df8224bd6fdd26e1fd90b84e73809a3b6969f9cb8a11d8f819f6837231044

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for orderedstructs-0.3.17-cp314-cp314-macosx_10_15_universal2.whl
Algorithm Hash digest
SHA256 ad4ee2efd47ef204fcae36646b8f5dbe3530651a42a389641385605f1887bde3
MD5 6d762248b5d91b859456c39ecae5dc2f
BLAKE2b-256 c95e29a22f47c21a24507c72bec56c66850c14231fc0acf4a68ca8437932f9d4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for orderedstructs-0.3.17-cp313-cp313-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 2e69773520b7de0835b73f96c0fba862fd2d696f638fc30c73287ff57184dcd3
MD5 7b0e6d37959513f2c11d9d3afe006690
BLAKE2b-256 9da19d3d17bb8b791e03b860f1a8116cd9bb169d7db41aa20e2eec8c9dda136c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for orderedstructs-0.3.17-cp312-cp312-macosx_10_13_universal2.whl
Algorithm Hash digest
SHA256 feefe4a2868bf4923d47f1c83d02408c9f12144933850cf100aaf26503262f6e
MD5 70513136d20a96ab6e42676e312c202a
BLAKE2b-256 e40f39dbd64e5489c3eb26a52781a0e3e04ef135cc8d6c41bca9b4a7ab5e0ed2

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for orderedstructs-0.3.17-cp311-cp311-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 24ddac0f87985b72c0c5eccdfe327037e3e41e1346c1caa3b84759b3c3a9049c
MD5 724c130dee715db842b58d5394928071
BLAKE2b-256 7b542d71c311fded5c2e0c7adb0b6f858af71305e84125e4240c1b191303d31a

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for orderedstructs-0.3.17-cp310-cp310-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 72bea190be80cad2a77ba375ff9bd028a4b8e1e55be7ed576d60aef1f5e0b709
MD5 baea9d91b6edeab931a4fcf5ee0ee943
BLAKE2b-256 55a3813c88bd5e34dede72c591618052280bfc93dac7930b9e2b1104dec86ce1

See more details on using hashes here.

File details

Details for the file orderedstructs-0.3.17-cp39-cp39-macosx_10_9_universal2.whl.

File metadata

File hashes

Hashes for orderedstructs-0.3.17-cp39-cp39-macosx_10_9_universal2.whl
Algorithm Hash digest
SHA256 7d0953bae94d3c1d8afb06576546dfc47360dee19c6b121952aadb412b6e2981
MD5 553a22f6ecae81bf8e4509843646a38d
BLAKE2b-256 8fbb943b29750c64944b74de3a764c1d19522140f2f1a2fce06f22b3b00d407f

See more details on using hashes here.

File details

Details for the file orderedstructs-0.3.17-cp38-cp38-macosx_11_0_universal2.whl.

File metadata

File hashes

Hashes for orderedstructs-0.3.17-cp38-cp38-macosx_11_0_universal2.whl
Algorithm Hash digest
SHA256 7eb273a6149e5a2f8ac9fc6c2a62944e06422c87203caa6d67e9f9da597b3cff
MD5 0526b6067de1c49816d09683bb9e683d
BLAKE2b-256 4dca64f8e0a6adc604dfbc1c71a028edc42c2e40a2a5ebbf5d463e5e9a9b41d4

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