A skip list is a probabilistic data structure that allows for efficient search, insertion, deletion, and other operations.
• It is similar to a linked list but with multiple layers of nodes, each providing skip pointers to nodes ahead in the list.
• This structure allows for faster search times compared to traditional linked lists and provides similar performance characteristics to balanced trees.
Here’s a brief overview of how skip lists work:
Nodes:
Each node in a skip list contains a key-value pair (or just a key, depending on the implementation) and pointers to nodes on the same level and nodes on lower levels.
Levels:
A skip list is composed of multiple levels, with the bottom level representing a sorted linked list of all the elements. Each higher level is a subset of the lower level, formed by “skipping” some elements.
Skip pointers:
Nodes at each level have pointers to nodes ahead in the list at the same level. These pointers allow for faster traversal during search operations.
Randomization:
Skip lists use randomization to determine which elements should have higher-level representations. This randomization ensures that the list remains balanced, with a logarithmic height.
Search:
Searching in a skip list is similar to searching in a balanced binary search tree. Starting from the top level, you traverse the list horizontally until you find a node with a key greater than or equal to the target key. Then, you move down to the next level and continue the search until you find the target key or determine that it doesn’t exist in the list.
Insertion and Deletion:
Insertion and deletion operations involve searching for the insertion or deletion point and adjusting pointers accordingly to maintain the skip list’s structure.
Importance of Skip List:
Skip lists are important data structures for several reasons:
Efficiency:
Skip lists offer efficient average-case time complexities for search, insertion, deletion, and other operations. While their worst-case time complexities are similar to those of balanced trees, skip lists are often easier to implement and maintain.
Simplicity:
Skip lists are simpler to implement compared to other balanced tree structures like AVL trees or Red-Black trees. They don’t require complex balancing algorithms, making them more accessible for developers to understand and work with.
Versatility:
Skip lists can be used in a variety of applications where efficient search, insertion, and deletion are required. They are particularly useful in scenarios where dynamic data structures are needed and the dataset is frequently updated.
No need for rebalancing:
Unlike balanced trees, skip lists do not require periodic rebalancing operations. This makes them well-suited for scenarios where frequent updates to the dataset occur.
Space Efficiency:
Skip lists can be more space-efficient compared to traditional balanced trees, especially when memory allocation overhead is taken into account.
Here is the basic implementation of skip List:
import java.util.Random;
class SkipListNode {
int value;
SkipListNode[] forward; // Array to hold references to the next nodes in each level
public SkipListNode(int value, int level) {
this.value = value;
this.forward = new SkipListNode[level + 1];
}
}
public class SkipList {
private static final int MAX_LEVEL = 5; // Maximum number of levels in the skip list
private int level; // Current maximum level of the skip list
private SkipListNode header; // Header node
public SkipList() {
level = 0;
header = new SkipListNode(Integer.MIN_VALUE, MAX_LEVEL);
for (int i = 0; i <= MAX_LEVEL; i++) {
header.forward[i] = null; // Initialize all forward references to null
}
}
// Generate a random level for a new node
private int randomLevel() {
Random rand = new Random();
int level = 0;
while (rand.nextDouble() < 0.5 && level < MAX_LEVEL) {
level++;
}
return level;
}
// Insert a new node into the skip list
public void insert(int value) {
SkipListNode[] update = new SkipListNode[MAX_LEVEL + 1];
SkipListNode current = header;
// Find the position to insert the new node
for (int i = level; i >= 0; i--) {
while (current.forward[i] != null && current.forward[i].value < value) {
current = current.forward[i];
}
update[i] = current;
}
current = current.forward[0];
// If the node with the given value already exists, do nothing
if (current == null || current.value != value) {
int newLevel = randomLevel();
if (newLevel > level) {
for (int i = level + 1; i <= newLevel; i++) {
update[i] = header;
}
level = newLevel;
}
// Create a new node with the given value
SkipListNode newNode = new SkipListNode(value, newLevel);
// Insert the new node into the skip list
for (int i = 0; i <= newLevel; i++) {
newNode.forward[i] = update[i].forward[i];
update[i].forward[i] = newNode;
}
}
}
// Search for a value in the skip list
public boolean search(int value) {
SkipListNode current = header;
for (int i = level; i >= 0; i--) {
while (current.forward[i] != null && current.forward[i].value < value) {
current = current.forward[i];
}
}
current = current.forward[0];
return current != null && current.value == value;
}
// Display the skip list
public void display() {
for (int i = 0; i <= level; i++) {
SkipListNode current = header.forward[i];
System.out.print("Level " + i + ": ");
while (current != null) {
System.out.print(current.value + " ");
current = current.forward[i];
}
System.out.println();
}
}
public static void main(String[] args) {
SkipList skipList = new SkipList();
skipList.insert(3);
skipList.insert(6);
skipList.insert(7);
skipList.insert(9);
skipList.insert(12);
skipList.insert(19);
skipList.insert(21);
skipList.insert(25);
skipList.insert(26);
skipList.insert(31);
System.out.println("Skip List after insertion:");
skipList.display();
System.out.println("\nSearch for 12: " + skipList.search(12));
System.out.println("Search for 20: " + skipList.search(20));
}
}