Skip lists are an advanced data structure that enables efficient search, insertion, and deletion operations in logarithmic time. Designed as an alternative to balanced trees, skip lists leverage randomization for maintaining balance, making them simpler and often faster in implementation.
In this guide, we will delve into the process of inserting an element into a skip list, breaking it down into comprehensive steps, explaining its algorithm, and providing implementation details in multiple programming languages like C Programming, C++ Programming, CSharp, Java Programming, JavaScript, and Python.
Also Read:
- Skip List Introduction – Efficient Search, Insert, and Delete in Linked List
- Searching and Deleting Elements in Skip Lists: A Comprehensive Guide
Table of Contents
Understanding the Structure of a Skip List
At its core, a skip list consists of nodes connected in a hierarchical linked structure, where each node carries a key and multiple forward pointers to other nodes. The organization of these pointers across various levels provides the skip list with characteristic efficiency.
Node Structure
Each node in a skip list is designed with two main components:
- Key: The value associated with the node.
- Forward Array: An array of pointers (or references) that point to other nodes at different levels.

The size of the forward array for each node is determined at the time of its insertion, and its level is chosen randomly using the randomLevel()
function. The higher the level of a node, the farther it can “skip” in the list, hence the name “skip list.”
Random Level Selection for Nodes
Algorithm
The efficiency of a skip list depends on the random levels assigned to nodes during their insertion. This randomness ensures a balanced structure without requiring complex rotations or restructuring as in AVL trees or Red-Black trees. Here is how the random level for a node is determined:
In Python Programming Language
def randomLevel():
lvl = 1
while random() < p and lvl < MaxLevel:
lvl += 1
return lvl
Explanation:
- The level starts at 1.
- A random number between 0 and 1 is generated (
random()
). - If the random number is less than a predefined constant
p
(probability factor) and the level is less thanMaxLevel
, the level is incremented. - This process continues until either the random condition fails or the maximum level is reached.
The constant p
determines the likelihood of a node having higher levels. For example, if p = 0.5
, about half of the nodes will have level 1, one-fourth will have level 2, and so on.
In Multiple Programming Languages
C Programming
#include <stdio.h>
#include <stdlib.h> // For rand()
int randomLevel(float p, int MaxLevel) {
int lvl = 1;
while ((float)rand() / RAND_MAX < p && lvl < MaxLevel) {
lvl++;
}
return lvl;
}
Explanation for C
rand()
generates a random integer, which is normalized to a range of[0, 1)
using(float)rand() / RAND_MAX
.- Compare the generated value with
p
. - Increment the
lvl
until the random condition fails orlvl
reachesMaxLevel
.
C++ Implementation
#include <iostream>
#include <cstdlib> // For rand()
int randomLevel(float p, int MaxLevel) {
int lvl = 1;
while ((float)rand() / RAND_MAX < p && lvl < MaxLevel) {
lvl++;
}
return lvl;
}
Explanation for C++
- Similar to the C implementation,
rand()
is used for random number generation. - The same logic for normalization, comparison, and level increment applies.
C# Implementation
using System;
class SkipList {
public static int RandomLevel(float p, int MaxLevel) {
Random random = new Random();
int lvl = 1;
while (random.NextDouble() < p && lvl < MaxLevel) {
lvl++;
}
return lvl;
}
}
Explanation for C#
- Use the
Random.NextDouble()
method to generate a random number between0
and1
. - The comparison and level increment logic remains consistent.
Java Implementation
import java.util.Random;
class SkipList {
public static int randomLevel(float p, int MaxLevel) {
Random random = new Random();
int lvl = 1;
while (random.nextDouble() < p && lvl < MaxLevel) {
lvl++;
}
return lvl;
}
}
Explanation for Java
- Use
Random.nextDouble()
for random number generation. - The comparison and increment logic follow the same pattern as in other languages.
JavaScript Implementation
function randomLevel(p, MaxLevel) {
let lvl = 1;
while (Math.random() < p && lvl < MaxLevel) {
lvl++;
}
return lvl;
}
Explanation for JavaScript
Math.random()
generates a random number between0
and1
.- Compare it with
p
, and increment the level until the conditions fail.
Python Implementation
import random
def randomLevel(p, MaxLevel):
lvl = 1
while random.random() < p and lvl < MaxLevel:
lvl += 1
return lvl
Explanation for Python
- Use
random.random()
to generate a value in the range[0, 1)
. - Compare it with
p
, and increment the level until the loop conditions fail.
Comparison of Implementations
- Random Number Generation:
- C, C++: Use
rand() / RAND_MAX
. - JavaScript, Python, Java, C#: Provide built-in methods for generating normalized random values.
- C, C++: Use
- Loop Condition:
- All implementations follow the same structure: compare
random()
result withp
and ensurelvl < MaxLevel
.
- All implementations follow the same structure: compare
- Return Value:
- All return the calculated
lvl
, ensuring uniformity across languages.
- All return the calculated
This algorithm is straightforward yet critical for ensuring the probabilistic balancing of a skip list, as it determines the height of each node based on randomization, controlled by the probability constant p
Determining Maximum Levels
The maximum level (MaxLevel
) is a theoretical upper bound on the levels a node can have. It is derived using the formula: L(N)=logp/2N
where N is the expected number of nodes in the list, and p is the probability factor.
Skip List Insertion Algorithm
Inserting an element into a skip list involves finding the appropriate position for the new key and updating the pointers of surrounding nodes to include the new node. The process can be broken down into several detailed steps:
Step 1: Searching for the Insertion Position
The search starts at the highest level of the skip list. For each level, we traverse forward until we find a node whose key is greater than or equal to the key being inserted or reaches the end of the list.
At each level, a pointer to the current node is stored in an updated array, which is later used for rearranging the forward references.
Step 2: Handling Level 0
Once the search reaches level 0, we find the exact position for the new key. If the key does not already exist in the list, we proceed with the insertion.
Step 3: Generating a Random Level
Using the randomLevel()
function, a random level is generated for the new node. If this level exceeds the current highest level in the list, the header node is updated to include references for the additional levels.
Step 4: Node Insertion and Pointer Updates
The new node is created with the chosen level and inserted into the list by updating the forward references of the nodes in the update
array. This step ensures that all pointers at relevant levels now include the new node.
Code Implementation In Multiple Programming Languages
Below is the Code implementation in multiple programming languages {Like: C Programming, C++ Programming, CSharp, Java Programming, JavaScript, and Python} of the skip list with insertion functionality:

C Programming
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
// Define the maximum level for the skip list
#define MAX_LEVEL 3
// Probability factor for level increment
#define P 0.5
// Node structure
typedef struct Node {
int key; // Key value of the node
struct Node **forward; // Array of forward pointers
} Node;
// Skip List structure
typedef struct SkipList {
int level; // Current maximum level of the skip list
Node *header; // Pointer to the header node
} SkipList;
// Function to create a new node
Node *createNode(int level, int key) {
Node *n = (Node *)malloc(sizeof(Node));
n->key = key;
// Allocate memory for forward pointers up to the specified level
n->forward = (Node **)malloc(sizeof(Node *) * (level + 1));
for (int i = 0; i <= level; i++) {
n->forward[i] = NULL;
}
return n;
}
// Function to create and initialize the skip list
SkipList *createSkipList() {
SkipList *sl = (SkipList *)malloc(sizeof(SkipList));
sl->level = 0;
// Create the header node with key -1 and maximum level
sl->header = createNode(MAX_LEVEL, -1);
return sl;
}
// Function to generate a random level for a node
int randomLevel() {
int level = 0;
while ((float)rand() / RAND_MAX < P && level < MAX_LEVEL) {
level++;
}
return level;
}
// Function to insert a key into the skip list
void insertElement(SkipList *sl, int key) {
// Array to track the update references for each level
Node *update[MAX_LEVEL + 1];
Node *current = sl->header;
// Start from the highest level and move forward
for (int i = sl->level; i >= 0; i--) {
while (current->forward[i] != NULL && current->forward[i]->key < key) {
current = current->forward[i];
}
update[i] = current; // Store the last accessed node at each level
}
// Move to the level 0 forward node
current = current->forward[0];
// If the key does not exist, insert it
if (current == NULL || current->key != key) {
// Generate a random level for the new node
int rlevel = randomLevel();
// Update the list's level if the new level is greater
if (rlevel > sl->level) {
for (int i = sl->level + 1; i <= rlevel; i++) {
update[i] = sl->header;
}
sl->level = rlevel;
}
// Create a new node with the generated random level
Node *n = createNode(rlevel, key);
// Update the forward pointers and insert the new node
for (int i = 0; i <= rlevel; i++) {
n->forward[i] = update[i]->forward[i];
update[i]->forward[i] = n;
}
printf("Successfully inserted key %d\n", key);
}
}
// Function to display the skip list
void displayList(SkipList *sl) {
printf("\n*****Skip List******\n");
for (int i = 0; i <= sl->level; i++) {
Node *node = sl->header->forward[i];
printf("Level %d: ", i);
while (node != NULL) {
printf("%d ", node->key);
node = node->forward[i];
}
printf("\n");
}
}
// Main driver function to test the skip list
int main() {
// Initialize random seed
srand((unsigned int)time(NULL));
// Create a skip list
SkipList *lst = createSkipList();
// Insert elements into the skip list
insertElement(lst, 3);
insertElement(lst, 6);
insertElement(lst, 7);
insertElement(lst, 9);
insertElement(lst, 12);
insertElement(lst, 19);
insertElement(lst, 17);
insertElement(lst, 26);
insertElement(lst, 21);
insertElement(lst, 25);
// Display the skip list
displayList(lst);
return 0;
}
Step-by-Step Explanation
- Node Structure
- A node is defined as a structure containing:
key
: The value stored in the node.forward
: An array of pointers to other nodes in different levels.
- A node is defined as a structure containing:
- Skip List Structure
- A skip list contains:
level
: The maximum level currently used in the skip list.header
: A dummy node that acts as the starting point for all levels.
- A skip list contains:
- Node Creation (
createNode
)- The
createNode
function allocates memory for a new node and its forward pointers. - The forward array is initialized with
NULL
to indicate no connections initially.
- The
- Skip List Initialization (
createSkipList
)- A skip list is initialized with:
level = 0
(no levels initially populated).- A header node with key
-1
and the maximum possible level.
- A skip list is initialized with:
- Random Level Generation (
randomLevel
)- The random level is generated probabilistically using the formula:
- Increment the level as long as a random number r<Pr < Pr<P and level <
MAX_LEVEL
.
- Increment the level as long as a random number r<Pr < Pr<P and level <
- The random level is generated probabilistically using the formula:
- Insertion Logic (
insertElement
)- Search for Position: Start from the highest level and move forward while the next key is smaller than the key to be inserted.
- Update Array: Track the last accessed nodes at each level.
- Create New Node: If the key is not found, create a new node and update the forward pointers in the update array.
- Display Function (
displayList
)- Traverses each level of the skip list and prints the keys.
- Main Function
- Initializes the skip list, inserts elements, and displays the structure.
C++ Implementation
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
// Node class definition
class Node {
public:
int key; // Key value of the node
Node **forward; // Array of forward pointers for different levels
// Constructor
Node(int key, int level) {
this->key = key;
// Allocate memory for forward pointers
forward = new Node*[level + 1];
// Initialize all forward pointers to NULL
memset(forward, 0, sizeof(Node*) * (level + 1));
}
};
// Skip List class definition
class SkipList {
int MAXLVL; // Maximum allowed level in the skip list
float P; // Probability for level increment
int level; // Current maximum level in the skip list
Node *header; // Pointer to the header node
public:
// Constructor
SkipList(int MAXLVL, float P) {
this->MAXLVL = MAXLVL;
this->P = P;
this->level = 0;
// Create header node with key -1
header = new Node(-1, MAXLVL);
}
// Function to generate a random level for a node
int randomLevel() {
int lvl = 0;
while (((float)rand() / RAND_MAX) < P && lvl < MAXLVL) {
lvl++;
}
return lvl;
}
// Function to create a new node
Node* createNode(int key, int level) {
return new Node(key, level);
}
// Function to insert a key into the skip list
void insertElement(int key) {
Node *current = header; // Start at the header node
// Array to track update references for each level
Node *update[MAXLVL + 1];
memset(update, 0, sizeof(Node*) * (MAXLVL + 1));
// Traverse each level from top to bottom
for (int i = level; i >= 0; i--) {
while (current->forward[i] != NULL && current->forward[i]->key < key) {
current = current->forward[i];
}
update[i] = current; // Store the last node visited at each level
}
// Move to the next node at level 0
current = current->forward[0];
// Check if the key already exists
if (current == NULL || current->key != key) {
// Generate a random level for the new node
int rlevel = randomLevel();
// Update the list level if the new level is higher
if (rlevel > level) {
for (int i = level + 1; i <= rlevel; i++) {
update[i] = header;
}
level = rlevel;
}
// Create a new node with the generated level
Node *n = createNode(key, rlevel);
// Insert the new node by adjusting forward pointers
for (int i = 0; i <= rlevel; i++) {
n->forward[i] = update[i]->forward[i];
update[i]->forward[i] = n;
}
cout << "Successfully Inserted key " << key << "\n";
}
}
// Function to display the skip list level by level
void displayList() {
cout << "\n*****Skip List*****\n";
for (int i = 0; i <= level; i++) {
Node *node = header->forward[i];
cout << "Level " << i << ": ";
while (node != NULL) {
cout << node->key << " ";
node = node->forward[i];
}
cout << "\n";
}
}
};
// Main function to test the implementation
int main() {
// Seed the random number generator
srand((unsigned)time(0));
// Create a SkipList object with maximum level 3 and probability 0.5
SkipList lst(3, 0.5);
// Insert elements into the skip list
lst.insertElement(3);
lst.insertElement(6);
lst.insertElement(7);
lst.insertElement(9);
lst.insertElement(12);
lst.insertElement(19);
lst.insertElement(17);
lst.insertElement(26);
lst.insertElement(21);
lst.insertElement(25);
// Display the skip list
lst.displayList();
return 0;
}
Detailed Explanation of Each Step
- Node Class
- Purpose: Represents an individual node in the skip list.
- Contains:
key
: The value stored in the node.forward
: A dynamically allocated array of pointers for different levels.
- Constructor:
- Allocates memory for
forward
pointers. - Initializes all pointers to
NULL
usingmemset
.
- Allocates memory for
- SkipList Class
- Purpose: Represents the overall skip list.
- Contains:
MAXLVL
: Maximum level a node can have.P
: Probability factor to decide whether a node moves to the next level.level
: Current highest level in the skip list.header
: A dummy node that acts as the starting point for all levels.
- Constructor:
- Initializes
MAXLVL
,P
, andlevel
. - Creates a header node with a key of
-1
.
- Initializes
- Random Level Generation
- Purpose: Generates a random level for a new node.
- Logic:
- Start with
lvl = 0
. - Increment the level if a random number is less than
P
andlvl < MAXLVL
.
- Start with
- Insert Element
- Logic:
- Search Position:
- Traverse from the highest level down to level 0.
- Stop when the next node’s key is greater than or equal to the key being inserted.
- Store the last visited nodes at each level in the
update
array.
- Check for Duplicates:
- If the key already exists, do nothing.
- Generate Random Level:
- Create a new level for the node.
- If this level is greater than the current level, update the header pointers for new levels.
- Insert Node:
- Create a new node and adjust pointers for all levels up to
rlevel
.
- Create a new node and adjust pointers for all levels up to
- Search Position:
- Logic:
- Display Skip List
- Logic:
- Traverse each level from 0 to the current maximum level.
- Print all keys present at each level.
- Logic:
C# Implementation
using System;
public class Node
{
public int Key; // The value stored in the node
public Node[] Forward; // Array of forward pointers at various levels
// Constructor to initialize the node
public Node(int key, int level)
{
Key = key;
Forward = new Node[level + 1]; // Allocate memory for forward pointers
}
}
public class SkipList
{
private readonly int MAX_LEVEL; // Maximum level for the skip list
private readonly float P; // Probability factor for level increase
private int level; // Current maximum level in the skip list
private readonly Node header; // Header node to represent the start of the skip list
// Constructor to initialize the skip list
public SkipList(int maxLevel, float probability)
{
MAX_LEVEL = maxLevel;
P = probability;
level = 0;
header = new Node(-1, MAX_LEVEL); // Initialize the header node with key -1
}
// Generate a random level for a new node
private int RandomLevel()
{
int lvl = 0;
Random random = new Random();
while (random.NextDouble() < P && lvl < MAX_LEVEL)
{
lvl++;
}
return lvl;
}
// Insert a new key into the skip list
public void InsertElement(int key)
{
Node current = header;
// Create an array to keep track of nodes at each level where pointers need to be updated
Node[] update = new Node[MAX_LEVEL + 1];
// Traverse the skip list from the top level down to level 0
for (int i = level; i >= 0; i--)
{
while (current.Forward[i] != null && current.Forward[i].Key < key)
{
current = current.Forward[i];
}
update[i] = current; // Store the node at this level
}
// Move to the next node at level 0
current = current.Forward[0];
// If the key doesn't already exist, insert it
if (current == null || current.Key != key)
{
int rLevel = RandomLevel(); // Generate a random level for the new node
// If the new node's level is greater than the current level, update levels
if (rLevel > level)
{
for (int i = level + 1; i <= rLevel; i++)
{
update[i] = header;
}
level = rLevel;
}
// Create the new node
Node newNode = new Node(key, rLevel);
// Adjust pointers to insert the new node
for (int i = 0; i <= rLevel; i++)
{
newNode.Forward[i] = update[i].Forward[i];
update[i].Forward[i] = newNode;
}
Console.WriteLine($"Successfully inserted key {key}");
}
}
// Display the skip list level by level
public void DisplayList()
{
Console.WriteLine("\n***** Skip List *****");
for (int i = 0; i <= level; i++)
{
Node node = header.Forward[i];
Console.Write($"Level {i}: ");
while (node != null)
{
Console.Write(node.Key + " ");
node = node.Forward[i];
}
Console.WriteLine();
}
}
// Driver code to test the skip list
public static void Main(string[] args)
{
SkipList skipList = new SkipList(3, 0.5f); // Create a skip list with a max level of 3 and probability 0.5
// Insert elements into the skip list
skipList.InsertElement(3);
skipList.InsertElement(6);
skipList.InsertElement(7);
skipList.InsertElement(9);
skipList.InsertElement(12);
skipList.InsertElement(19);
skipList.InsertElement(17);
skipList.InsertElement(26);
skipList.InsertElement(21);
skipList.InsertElement(25);
// Display the skip list
skipList.DisplayList();
}
}
Step-by-Step Explanation
- 1. Node Class
- Represents an individual node in the skip list.
- Attributes:
Key
: Stores the value of the node.Forward
: Array of pointers for all possible levels of the skip list.
- Constructor:
- Allocates memory for the
Forward
array, based on the level of the node.
- Allocates memory for the
- 2. SkipList Class
- Represents the overall skip list structure.
- Attributes:
MAX_LEVEL
: Defines the maximum number of levels the skip list can have.P
: The probability that determines whether a new node goes to the next level.level
: Tracks the current highest level in the skip list.header
: A dummy node with key-1
to serve as the starting point.
- 3. Random Level Generation
- The method
RandomLevel()
generates a random level for a new node. - Logic:
- Starts at level
0
. - Increments the level while a random number is less than
P
and the level is belowMAX_LEVEL
.
- Starts at level
- The method
- 4. Inserting a New Element
- Initialization:
- Starts with the
header
node. - Creates an array
update[]
to keep track of nodes whoseForward
pointers need to be updated for each level.
- Starts with the
- Traversal:
- From the highest level down to level
0
, moves forward while the next node’s key is less than the key being inserted. - Stores the node at each level in the
update[]
array.
- From the highest level down to level
- Insertion Check:
- Checks if the key already exists in the skip list (at level
0
). - If the key exists, no insertion is performed.
- Checks if the key already exists in the skip list (at level
- Random Level Assignment:
- Generates a random level
rLevel
for the new node. - Updates the
level
of the skip list ifrLevel
is greater than the current level.
- Generates a random level
- Node Creation:
- Creates a new node with the generated
rLevel
.
- Creates a new node with the generated
- Pointer Adjustment:
- Updates the
Forward
pointers of the nodes in theupdate[]
array to point to the new node.
- Updates the
- Initialization:
- 5. Displaying the Skip List
- The
DisplayList()
method prints the skip list level by level. - Traverses each level and prints the keys of the nodes at that level.
- The
Java Implementation
import java.util.Arrays;
public class SkipListExample {
// Class to implement Node
static class Node {
int key; // Key value of the node
Node[] forward; // Array of forward pointers for different levels
// Constructor to initialize the node
Node(int key, int level) {
this.key = key;
this.forward = new Node[level + 1]; // Initialize forward pointers
Arrays.fill(forward, null); // Set all forward pointers to null
}
}
// Class for SkipList
static class SkipList {
private final int MAXLVL; // Maximum level for the skip list
private final float P; // Probability factor
private int level; // Current maximum level of the skip list
private final Node header; // Header node
// Constructor to initialize skip list
public SkipList(int maxLevel, float probability) {
this.MAXLVL = maxLevel;
this.P = probability;
this.level = 0;
this.header = new Node(-1, MAXLVL); // Header node with key -1
}
// Generate a random level for the new node
private int randomLevel() {
int lvl = 0;
while (Math.random() < P && lvl < MAXLVL) {
lvl++;
}
return lvl;
}
// Insert a key into the skip list
public void insertElement(int key) {
Node current = header;
// Update array to keep track of previous nodes at each level
Node[] update = new Node[MAXLVL + 1];
Arrays.fill(update, null);
// Traverse the skip list from the highest level to level 0
for (int i = level; i >= 0; i--) {
while (current.forward[i] != null && current.forward[i].key < key) {
current = current.forward[i];
}
update[i] = current; // Track the node at each level
}
// Move to the next node at level 0
current = current.forward[0];
// If the key doesn't already exist, insert a new node
if (current == null || current.key != key) {
int rlevel = randomLevel(); // Generate a random level for the new node
// Update the current level if the new node's level is higher
if (rlevel > level) {
for (int i = level + 1; i <= rlevel; i++) {
update[i] = header;
}
level = rlevel;
}
// Create a new node with the generated level
Node newNode = new Node(key, rlevel);
// Adjust pointers to insert the new node
for (int i = 0; i <= rlevel; i++) {
newNode.forward[i] = update[i].forward[i];
update[i].forward[i] = newNode;
}
System.out.println("Successfully Inserted key " + key);
}
}
// Display the skip list level by level
public void displayList() {
System.out.println("\n***** Skip List *****");
for (int i = 0; i <= level; i++) {
Node node = header.forward[i];
System.out.print("Level " + i + ": ");
while (node != null) {
System.out.print(node.key + " ");
node = node.forward[i];
}
System.out.println();
}
}
}
// Main method to test the skip list
public static void main(String[] args) {
SkipList skipList = new SkipList(3, 0.5f);
// Insert elements into the skip list
skipList.insertElement(3);
skipList.insertElement(6);
skipList.insertElement(7);
skipList.insertElement(9);
skipList.insertElement(12);
skipList.insertElement(19);
skipList.insertElement(17);
skipList.insertElement(26);
skipList.insertElement(21);
skipList.insertElement(25);
// Display the skip list
skipList.displayList();
}
}
Step-by-Step Explanation
- 1. Node Class
- Purpose: Represents an individual node in the skip list.
- Attributes:
key
: The value stored in the node.forward
: An array of pointers that link nodes across multiple levels.
- Constructor:
- Initializes the
key
and allocates memory for theforward
array, setting all pointers tonull
.
- Initializes the
- 2. SkipList Class
- Purpose: Represents the overall skip list structure.
- Attributes:
MAXLVL
: The maximum number of levels in the skip list.P
: The probability that determines the level of a new node.level
: Tracks the current highest level in the skip list.header
: A dummy node that serves as the starting point for all levels.
- Constructor:
- Sets the
MAXLVL
andP
values. - Creates a header node with a key of
-1
.
- Sets the
- 3. Random Level Generation
- Logic:
- Starts with
lvl = 0
. - Increments
lvl
while a random number is less thanP
andlvl
is less thanMAXLVL
.
- Starts with
- Purpose:
- Determines the height of a new node probabilistically.
- Logic:
- 4. Insert Element
- Logic:
- Traversal:
- Starts from the highest level and moves forward while the next node’s key is smaller than the key being inserted.
- Tracks the previous nodes at each level using the
update
array.
- Insertion Check:
- If the key already exists, no insertion is performed.
- Random Level:
- Generates a random level for the new node.
- If the new level is higher than the current level, the
update
array is adjusted to include the header node at the new levels.
- Node Creation:
- A new node is created with the randomly generated level.
- Pointer Adjustment:
- Updates the forward pointers of the
update
nodes to link the new node at each level.
- Updates the forward pointers of the
- Traversal:
- Logic:
- 5. Display List
- Logic:
- Iterates over each level from 0 to the current highest level.
- Prints the keys of all nodes at that level.
- Logic:
JavaScript Implementation
// Node class represents an individual node in the skip list
class Node {
constructor(key, level) {
this.key = key; // The value stored in the node
this.forward = new Array(level + 1).fill(null); // Array to store pointers for different levels
}
}
// SkipList class represents the overall skip list
class SkipList {
constructor(MAXLVL, P) {
this.MAXLVL = MAXLVL; // Maximum levels for the skip list
this.P = P; // Probability factor for level increase
this.level = 0; // Current maximum level of the skip list
this.header = new Node(-1, MAXLVL); // Header node with key -1 as the starting point
}
// Generates a random level for a new node
randomLevel() {
let lvl = 0;
while (Math.random() < this.P && lvl < this.MAXLVL) {
lvl++;
}
return lvl;
}
// Creates a new node with the given key and level
createNode(key, level) {
return new Node(key, level);
}
// Inserts a given key into the skip list
insertElement(key) {
let current = this.header;
// Create an array to keep track of nodes at each level where pointers need to be updated
let update = new Array(this.MAXLVL + 1).fill(null);
// Traverse the skip list from the highest level down to level 0
for (let i = this.level; i >= 0; i--) {
while (current.forward[i] !== null && current.forward[i].key < key) {
current = current.forward[i];
}
update[i] = current; // Store the node at this level
}
// Move to the next node at level 0
current = current.forward[0];
// If the key doesn't already exist, insert it
if (current === null || current.key !== key) {
let rLevel = this.randomLevel(); // Generate a random level for the new node
// If the random level is greater than the current skip list level
if (rLevel > this.level) {
for (let i = this.level + 1; i <= rLevel; i++) {
update[i] = this.header;
}
this.level = rLevel; // Update the current maximum level
}
// Create the new node
let newNode = this.createNode(key, rLevel);
// Adjust pointers at all levels up to rLevel
for (let i = 0; i <= rLevel; i++) {
newNode.forward[i] = update[i].forward[i];
update[i].forward[i] = newNode;
}
console.log(`Successfully inserted key ${key}`);
}
}
// Displays the skip list level by level
displayList() {
console.log("\n***** Skip List *****");
for (let i = 0; i <= this.level; i++) {
let node = this.header.forward[i];
let levelStr = `Level ${i}: `;
while (node !== null) {
levelStr += `${node.key} `;
node = node.forward[i];
}
console.log(levelStr);
}
}
}
// Driver code to test the skip list
const skipList = new SkipList(3, 0.5); // Create a skip list with max level 3 and probability 0.5
// Insert elements into the skip list
skipList.insertElement(3);
skipList.insertElement(6);
skipList.insertElement(7);
skipList.insertElement(9);
skipList.insertElement(12);
skipList.insertElement(19);
skipList.insertElement(17);
skipList.insertElement(26);
skipList.insertElement(21);
skipList.insertElement(25);
// Display the skip list
skipList.displayList();
Explanation for JavaScript
- Node Class
- The
Node
class defines the structure of a node in the skip list:key
: Holds the value stored in the node.forward
: An array of pointers, one for each level. Initially, all pointers are set tonull
.
- The
- SkipList Class
- The
SkipList
class defines the overall skip list structure:MAXLVL
: Maximum levels the skip list can have.P
: A probability factor to determine whether a new node should go to the next level.level
: The current maximum level in the skip list.header
: A dummy node at the start of the skip list to make traversal easier.
- The
- Random Level Generation
- The
randomLevel
method generates a random level for a new node:- Starts at level 0.
- While a random number is less than
P
and the level is less thanMAXLVL
, increments the level.
- The
- Inserting a New Element
- The
insertElement
method performs the insertion:- Initialization:
- Starts with the
header
node. - Creates an
update
array to keep track of nodes whoseforward
pointers need to be updated.
- Starts with the
- Traversal:
- Traverses each level of the skip list from the highest to the lowest.
- Moves forward as long as the next node’s key is less than the key being inserted.
- Stores the node at each level in the
update
array.
- Insertion Check:
- If the key doesn’t exist (either
current
isnull
orcurrent.key
is not equal to the key), proceeds with the insertion.
- If the key doesn’t exist (either
- Random Level Assignment:
- Generates a random level for the new node.
- Updates the
level
of the skip list if the random level is higher than the current level.
- Node Creation:
- Creates a new node with the generated random level and the given key.
- Pointer Adjustment:
- Updates the
forward
pointers of nodes in theupdate
array to include the new node.
- Updates the
- Initialization:
- The
- Displaying the Skip List
- The
displayList
method prints the skip list level by level:- Traverses each level of the skip list, printing the keys of the nodes.
- The
Python Implementation
import random
class Node:
'''
Class to implement a node in the Skip List.
Each node contains a key and a list of forward references for different levels.
'''
def __init__(self, key, level):
self.key = key # The value stored in the node
self.forward = [None] * (level + 1) # List to hold references for different levels
class SkipList:
'''
Class to implement the Skip List.
It supports insertion and displaying the skip list level-wise.
'''
def __init__(self, max_lvl, P):
# Maximum level for the skip list
self.MAXLVL = max_lvl
# Probability factor (P) determines the likelihood of nodes having higher levels.
self.P = P
# Create a header node with a maximum level and initialize its key to -1
self.header = self.createNode(self.MAXLVL, -1)
# Current level of the skip list (starts at level 0)
self.level = 0
def createNode(self, lvl, key):
'''Create and return a new node with the given key and level.'''
return Node(key, lvl)
def randomLevel(self):
'''Generate a random level for a new node, based on probability P.'''
lvl = 0
while random.random() < self.P and lvl < self.MAXLVL:
lvl += 1
return lvl
def insertElement(self, key):
'''
Insert the given key into the skip list.
The insertion is done at the appropriate position based on the key value.
'''
# Create an array to keep track of nodes at each level where pointers need to be updated
update = [None] * (self.MAXLVL + 1)
current = self.header
# Traverse the skip list starting from the highest level
for i in range(self.level, -1, -1):
while current.forward[i] and current.forward[i].key < key:
current = current.forward[i]
update[i] = current # Store the node at this level
# Move to the next node at level 0
current = current.forward[0]
# If the key is not found (or reached the end of the list), insert the new node
if current is None or current.key != key:
# Generate a random level for the new node
rlevel = self.randomLevel()
# If the random level is higher than the current maximum level of the skip list
if rlevel > self.level:
# Update the skip list's level and initialize the new levels
for i in range(self.level + 1, rlevel + 1):
update[i] = self.header
self.level = rlevel
# Create the new node with the randomly generated level
new_node = self.createNode(rlevel, key)
# Update the pointers for all levels up to the new node's level
for i in range(rlevel + 1):
new_node.forward[i] = update[i].forward[i]
update[i].forward[i] = new_node
print(f"Successfully inserted key {key}")
def displayList(self):
'''Display the skip list level by level.'''
print("\n***** Skip List *****")
head = self.header
for lvl in range(self.level + 1):
print(f"Level {lvl}: ", end=" ")
node = head.forward[lvl]
while node:
print(node.key, end=" ")
node = node.forward[lvl]
print("") # Move to the next line for the next level
# Driver code to test the SkipList class
def main():
# Create a SkipList object with maximum level 3 and probability factor 0.5
skip_list = SkipList(3, 0.5)
# Insert elements into the skip list
skip_list.insertElement(3)
skip_list.insertElement(6)
skip_list.insertElement(7)
skip_list.insertElement(9)
skip_list.insertElement(12)
skip_list.insertElement(19)
skip_list.insertElement(17)
skip_list.insertElement(26)
skip_list.insertElement(21)
skip_list.insertElement(25)
# Display the skip list
skip_list.displayList()
# Execute the main function
if __name__ == "__main__":
main()
Step-by-Step Explanation
- Node Class
- The
Node
class represents an individual node in the skip list. Each node contains:key
: The value stored in the node.forward
: A list that holds references to the next node at each level. This allows skipping over multiple nodes in one step, facilitating faster searches and insertions.
- The
- SkipList Class
- The
SkipList
class manages the skip list operations such as insertion and traversal.MAXLVL
: Defines the maximum level that the skip list can have.P
: The probability that a new node will have a higher level (used to determine the node’s level).header
: A dummy node that marks the start of the skip list.level
: Tracks the current maximum level in the skip list.
- Methods in SkipList
createNode(level, key)
: This method creates a new node with the specified level and key.randomLevel()
: This method generates a random level for a new node based on the probabilityP
.
insertElement(key)
: This method inserts a new key into the skip list. It:- Starts from the highest level.
- Traverses down to the correct position where the new node should be inserted.
- If the node does not already exist, it generates a random level and inserts the node at the correct position across all levels.
displayList()
: This method prints the skip list level by level, showing the keys in each level.
- The
- Insertion Process
- The
insertElement
method inserts a new element in the skip list:- It traverses the list from the highest level down to level 0, checking for nodes that have a smaller key.
- Once the correct position is found, it inserts the new node.
- If the random level generated for the new node is higher than the current maximum level of the skip list, it updates the maximum level and adjusts the forward pointers accordingly.
- The
- Displaying the Skip List
- The
displayList
method prints the elements of the skip list level by level:- It starts from level 0 and prints the keys of the nodes at each level.
- For each level, it follows the
forward
pointers and prints the keys until it reaches the end (i.e.,None
).
- The
Output
Successfully inserted key 3
Successfully inserted key 6
Successfully inserted key 7
Successfully inserted key 9
Successfully inserted key 12
Successfully inserted key 19
Successfully inserted key 17
Successfully inserted key 26
Successfully inserted key 21
Successfully inserted key 25
***** Skip List *****
Level 0: 3 6 7 9 12 17 19 21 25 26
Level 1: 3 7 12 17 19 25
Level 2: 3 12 19
Note: The level of nodes is decided randomly, so output may differ.
Time Complexity
- Average: O(log(n))
- Worst: O(n)
Conclusion
The skip list is a versatile data structure that balances simplicity and performance. Using randomization achieves logarithmic efficiency without complex balancing mechanisms. Understanding the insertion process, especially the concepts of random levels and pointer updates, is crucial for mastering skip lists. The Python implementation provided here serves as a foundation for experimenting with and extending this fascinating structure.
Video Links Related to Skip List
- Skip List Explained – Advanced Data Structure
- Skip List – Efficient Search in Sorted Linked List
- Skip Lists Explained – Insertion and Deletion
- Skip Lists Explained – Searching
Related Articles
- Skip List Introduction – Efficient Search, Insert, and Delete in Linked List
- Skip List Insertion: A Comprehensive Exploration
- Searching and Deleting Elements in Skip Lists: A Comprehensive Guide
Read More Articles
- Data Structure (DS) Array:
- Why the Analysis of Algorithms is Important?
- Worst, Average, and Best Case Analysis of Algorithms: A Comprehensive Guide
- Understanding Pointers in C Programming: A Comprehensive Guide
- Understanding Arrays in Data Structures: A Comprehensive Exploration
- Memory Allocation of an Array: An In-Depth Comprehensive Exploration
- Understanding Basic Operations in Arrays: A Comprehensive Guide
- Understanding 2D Arrays in Programming: A Comprehensive Guide
- Mapping a 2D Array to a 1D Array: A Comprehensive Exploration
- Data Structure Linked List:
- Understanding Linked Lists in Data Structures: A Comprehensive Exploration
- Types of Linked List: Detailed Exploration, Representations, and Implementations
- Understanding Singly Linked Lists: A Detailed Exploration
- Understanding Doubly Linked List: A Comprehensive Guide
- Operations of Doubly Linked List with Implementation: A Detailed Exploration
- Insertion in Doubly Linked List with Implementation: A Detailed Exploration
- Inserting a Node at the beginning of a Doubly Linked List: A Detailed Exploration
- Inserting a Node After a Given Node in a Doubly Linked List: A Detailed Exploration
- Inserting a Node Before a Given Node in a Doubly Linked List: A Detailed Exploration
- Inserting a Node at a Specific Position in a Doubly Linked List: A Detailed Exploration
- Inserting a New Node at the End of a Doubly Linked List: A Detailed Exploration
- Deletion in a Doubly Linked List with Implementation: A Comprehensive Guide
- Deletion at the Beginning in a Doubly Linked List: A Detailed Exploration
- Deletion after a given node in Doubly Linked List: A Comprehensive Guide
- Deleting a Node Before a Given Node in a Doubly Linked List: A Detailed Exploration
- Deletion at a Specific Position in a Doubly Linked List: A Detailed Exploration
- Deletion at the End in Doubly Linked List: A Comprehensive Exploration
- Introduction to Circular Linked Lists: A Comprehensive Guide
- Understanding Circular Singly Linked Lists: A Comprehensive Guide
- Circular Doubly Linked List: A Comprehensive Guide
- Insertion in Circular Singly Linked List: A Comprehensive Guide
- Insertion in an Empty Circular Linked List: A Detailed Exploration
- Insertion at the Beginning in Circular Linked List: A Detailed Exploration
- Insertion at the End of a Circular Linked List: A Comprehensive Guide
- Insertion at a Specific Position in a Circular Linked List: A Detailed Exploration
- Deletion from a Circular Linked List: A Comprehensive Guide
- Deletion from the Beginning of a Circular Linked List: A Detailed Exploration
- Deletion at Specific Position in Circular Linked List: A Detailed Exploration
- Deletion at the End of a Circular Linked List: A Comprehensive Guide
- Searching in a Circular Linked List: A Comprehensive Exploration
- Advantages & Disadvantages of Circular Linked Lists: A Detailed Exploration
- Applications of Circular Linked Lists: A Detailed Exploration
- Skip List Introduction – Efficient Search, Insert, and Delete in Linked List
- Skip List Insertion: A Comprehensive Exploration
Frequently Asked Questions (FAQs)
What is a Skip List and how does it differ from traditional linked lists?
A skip list is a data structure that enhances the efficiency of basic linked lists by adding additional levels of nodes, allowing for faster search, insertion, and deletion operations. Unlike a regular singly linked list, which requires traversing all nodes one by one from the head to the tail, a skip list uses multiple levels of linked lists to provide shortcuts across the list. Each node in a skip list holds a key and multiple forward pointers that point to nodes at higher levels, creating a hierarchy.
The difference between a skip list and a traditional linked list is that the latter operates in O(n) time for search and insertion operations, while a skip list can achieve an average time complexity of O(log n) for these operations. This is because the additional levels in the skip list allow the search to “skip” over many nodes, drastically reducing the number of comparisons needed to find or insert a key.
Skip lists are useful for implementing sorted lists, priority queues, and even indexed databases. Their probabilistic nature allows for self-balancing behavior, which avoids the need for complex rebalancing algorithms like those used in binary search trees.
How does the insertion process in a skip list work?
The insertion process in a skip list involves several key steps to ensure that the list remains efficient and properly sorted after each insertion. Here’s a step-by-step breakdown:
- Start at the highest level: To insert a new key, we begin at the highest level of the skip list (the level with the most nodes).
- Search for the correct position: Traverse the list level by level, from top to bottom. At each level, you compare the key to the node’s key and move forward to the next node if the key is smaller. This process continues until you find the position where the new key should be inserted.
- Update the pointers: Once you find the correct position, update the forward pointers of the affected nodes (those that precede and follow the new key) to include the new node.
- Generate a random level: The new node’s level is determined randomly, based on the probability factor P. This level determines how many levels the new node will span in the skip list.
- Adjust the skip list level: If the generated level for the new node is greater than the current level of the skip list, the list’s maximum level is updated.
- Insert the node: Finally, the new node is inserted into the list at the appropriate position, and the forward pointers are adjusted at each level to include the new node.
The average time complexity of this operation is O(log n), making skip lists more efficient than regular linked lists for insertion.
What is the significance of the random level generation in a skip list?
The random level generation is a core feature of a skip list that determines how the list’s levels are populated and how efficient the data structure will be for searching and inserting elements. In the skip list, each new node is assigned a level based on a random process. This level is generated by simulating a coin flip or using a random number generator, where the probability P dictates how likely a node is to have a higher level.
This probabilistic approach helps balance the skip list without requiring explicit rebalancing or maintenance, which would be necessary in structures like binary search trees. The random level generation ensures that the skip list remains relatively balanced, with the average height of the list staying logarithmic (i.e., O(log n)).
For example, if the probability factor P is set to 0.5, there’s a 50% chance that a new node will have an additional level. As a result, half of the nodes will have level 1, a quarter will have level 2, an eighth will have level 3, and so on. This random distribution of nodes across levels ensures that the list has a small number of high-level nodes, creating shortcuts that improve performance.
How does the skip list handle collisions when inserting elements?
In a skip list, collisions are handled by simply checking if a node with the same key already exists at the desired position. If a node with the same key is found during the insertion process (at level 0, the bottom-most level), the insertion is aborted, and no new node is added. This ensures that each key in the skip list is unique.
This approach is similar to hash tables, where an insertion only happens if the key is not already present. However, in a skip list, the search process is still probabilistic and depends on the level of the node being inserted. If a node with the same key is found at any level, the skip list avoids duplication and maintains the integrity of the data structure.
What are the advantages of skip lists over balanced trees?
Skip lists have several advantages over balanced trees like AVL trees and Red-Black trees:
- Simplicity: Skip lists are conceptually simpler than balanced trees. Balancing in trees involves maintaining strict invariants and performing complex rotations during insertions and deletions. Skip lists, on the other hand, rely on a random process for balancing, making them easier to implement and maintain.
- Probabilistic nature: Skip lists do not require strict rules for balancing, as they rely on probabilistic behavior to maintain a logarithmic height. This makes skip lists less complex than traditional balanced trees, which require explicit rebalancing.
- Better cache locality: The linear structure of skip lists (due to their linked list nature) leads to better cache locality than tree-based structures, where nodes are scattered across memory. This can improve performance in certain cases, especially with large datasets.
- Insertion/Deletion efficiency: Skip lists offer O(log n) average time complexity for insertion and deletion operations, just like balanced trees, but without the need for complex balancing operations.
However, skip lists can have higher constant factors than balanced trees in some cases, and their memory usage may be higher due to the additional pointers for each level.
What is the time complexity of searching for an element in a skip list?
The search time complexity of a skip list is O(log n) on average, where n is the number of elements in the list. This is because, at each level of the skip list, we can skip over multiple nodes using the forward pointers, effectively halving the number of nodes we need to examine at each level.
In the worst case, the search will require traversing all the levels, but this happens rarely because the random level generation ensures that most elements will have fewer levels, keeping the average search time logarithmic.
- Best case: O(1) if the element is at the top-most level and the first node checked is the desired node.
- Average case: O(log n) due to the random nature of the levels, providing an efficient search.
- Worst case: O(n), which occurs when the skip list degenerates into a single-level list, which is rare.
Can skip lists be used for applications that require sorting or priority queues?
Yes, skip lists are particularly well-suited for applications that require sorted data or priority queues. Since skip lists maintain the elements in sorted order, they provide efficient operations for both insertion and deletion of the minimum or maximum element (depending on how the skip list is implemented).
For priority queues, skip lists can offer O(log n) time complexity for both insertion and removal of the highest-priority element. The skip list structure allows quick access to the front of the list (the lowest or highest value), and because elements are kept sorted, retrieving the minimum or maximum element is efficient.
How does the skip list maintain its balance without explicit rebalancing?
A skip list maintains balance probabilistically, relying on the random level generation process. When inserting a new element, a random number of levels is chosen for the node based on the probability factor P. Over time, this random process ensures that the number of nodes at each level decreases exponentially, following a geometric distribution.
The higher levels of the skip list will contain fewer nodes, but each node in those levels acts as a shortcut, speeding up search operations. This self-balancing feature is what differentiates skip lists from other data structures that require explicit rebalancing, such as AVL trees or Red-Black trees.
What are the memory requirements for a skip list?
The memory requirements of a skip list are determined by two factors:
- The number of nodes: Each node in the skip list requires storage for its key and forward pointers. The total number of nodes is proportional to the number of elements being stored in the list.
- The number of levels: Each node can have multiple forward pointers, and the total number of forward pointers for all nodes depends on the random level distribution. Since the number of pointers per node grows with the level, skip lists may require more memory than simple linked lists. On average, the memory used for the pointers is proportional to the logarithm of the number of elements in the skip list.
Thus, the overall space complexity is O(n log n), where n is the number of elements in the skip list.
How are skip lists implemented in programming languages like Python, Java, and C++?
In programming languages like Python, Java, and C++, skip lists can be implemented in a similar fashion by creating classes for the node and skip list.
- In Python, a
Node
class can be defined with attributes for the key and the forward pointers, while the SkipList class manages the overall skip list and the insertion process. - In Java and C++, the structure is almost identical, though the syntax differs. Java uses object-oriented principles with classes and arrays for the forward pointers. In C++, vectors or arrays are often used for the forward pointers, and pointers are managed more manually.
Regardless of the language, the core concept remains the same: create a node with forward pointers, traverse the levels during insertion, and generate random levels for each node.