Python Skip List Implementation: Multi-Level Index Optimization

Introduction

In our previous exploration, we covered a basic Skip List implementation with a single skip step of 2. While functional, this basic version doesn't fully leverage the power of skip lists. A true skip list employs multiple skip levels, allowing for even faster lookups by skipping over more elements. This article demonstrates how to enhance our skip list with multiple index levels, creating a more sophisticated and efficient data structure. Redefining Node Structure

The fundamental change begins with the node definition. Since we need to support multiple skip levels, storing a single index reference won't suffice. We replace the single index reference with a list that can hold multiple index pointers: ``` class SkipNode: def init(self, data, prev=None, next_node=None, indices=None): self.data = data self.prev = prev self.next_node = next_node self.indices = indices if indices is not None else []


This pattern demonstrates Python's conditional expression syntax, which differs from Java's ternary operator. The format `value1 if condition else value2` achieves the same result as `condition ? value1 : value2` in Java. Insertion Operation
-------------------

The insertion logic remains largely unchanged from our basic implementation. We locate the appropriate position and insert the new node. The key difference lies in how we handle the index reconstruction: ```
def add_node(new_node):
    if start.next_node is None:
        start.next_node = new_node
        new_node.next_node = end
        new_node.prev = start
        end.prev = new_node
        return
    
    current = start.next_node
    while current.next_node is not None or current == end:
        if current.data > new_node.data or current == end:
            predecessor = current.prev
            predecessor.next_node = new_node
            current.prev = new_node
            new_node.prev = predecessor
            new_node.next_node = current
            break
        current = current.next_node
    
    rebuild_indices_advanced()

Multi-Level Index Reconstruction

Our enhanced index rebuilding method creates two skip levels: one at every 2nd node and another at every 4th node: ``` def rebuild_indices_advanced(): primary_step = 2 secondary_step = 4

primary_index_tracker = start.next_node
secondary_index_tracker = start.next_node
current = start.next_node

while current.next_node is not None:
    current.indices = []
    
    if primary_step == 0:
        primary_step = 2
        primary_index_tracker.indices.append(current)
        primary_index_tracker = current
        
    if secondary_step == 0:
        secondary_step = 4
        secondary_index_tracker.indices.append(current)
        secondary_index_tracker = current
        
    current = current.next_node
    primary_step -= 1
    secondary_step -= 1

Optimized Search Algorithm
--------------------------

The search operation now leverages our multi-level indices. We begin with the highest-level index and progressively move to lower levels when our jump overshoots the target: ```
def find_value(target):
    current = start.next_node
    comparison_count = 0
    
    while current.next_node is not None:
        comparison_count += 1
        
        if target == current.data:
            print(f"Value found after {comparison_count} comparisons")
            return
        elif target < current.data:
            print(f"Value not found after {comparison_count} comparisons")
            return
        
        if current.indices:
            for idx in range(len(current.indices) - 1, -1, -1):
                if target > current.indices[idx].data:
                    current = current.indices[idx]
                    break
            else:
                current = current.next_node
        else:
            current = current.next_node
    
    print(f"Value not found after {comparison_count} comparisons")

Visualization Helper

For debugging and understanding the structure, here's a method to display the skip list with its indices: ``` def display_structure(): result = [] current = start.next_node

while current.next_node is not None:
    if current.indices:
        entry = {
            "data": current.data,
            "indices": [node.data for node in current.indices]
        }
    else:
        entry = {"data": current.data, "indices": current.indices}
    result.append(entry)
    current = current.next_node

for item in result:
    print(item)

Dynamic Step Calculation
------------------------

To create a truly adaptive skip list, we can calculate skip levels dynamically based on the total number of elements. First, we maintain a counter for the list size: ```
def add_node(new_node):
    global element_count
    element_count += 1
    
    if start.next_node is None:
        # Insertion logic continues...

Using the global keyword is essential when modifying a top-level varible from within a function, similar to how static variables work in other languages. Now we implement the dynamic index reconstruction: ``` def rebuild_indices_dynamic(): size = element_count step_mapping = {} step_values = []

# Calculate logarithmic skip levels
while int((size / 2)) > 1:
    size = int((size / 2))
    key = f"level_{size}"
    step_mapping[key] = start.next_node
    step_values.append(size)

step_backup = list(step_values)
current = start.next_node

while current.next_node is not None:
    current.indices = []
    
    for i in range(len(step_values)):
        step_values[i] -= 1
        
        if step_values[i] == 0:
            step_values[i] = step_backup[i]
            index_key = f"level_{step_backup[i]}"
            index_node = step_mapping[index_key]
            index_node.indices.append(current)
            step_mapping[index_key] = current
    
    current = current.next_node

Performance Analysis
--------------------

With our multi-level skip list, we observe significant improvements in search performance. In testing scenarios, we've reduced the number of comparisons needed to locate elements. While the actual performance varies based on data distribution, this implementation handles worst-case scenarios reasonably well. Future Improvements
-------------------

Several optimization opportunities remain for this implementation: - Implementing lazy deletion instead of rebuilding indices after every removal - Caching index structures to avoid complete reconstruction - Exploring altenrative step calculation algorithms - Adding concurrent access support for multi-threaded environments The skip list remains an elegant data structure balancing simplicity with performance, making it valuable for ordered collections requiring efficient lookups.

Tags: skip-list python data-structures algorithms performance-optimization

Posted on Wed, 14 Oct 2026 16:01:22 +0000 by ev66