🧠
Skiplist with Dynamic Max Level and Probability Tuning
💡 This approach improves flexibility by dynamically adjusting max levels based on size and tuning promotion probability for better performance.
Intuition
Instead of fixed max level, calculate max level from current size to optimize space and speed. Adjust promotion probability accordingly.
Algorithm
- Maintain current size of Skiplist and compute max level as log2(size).
- Adjust promotion probability to 0.5 or other values for balancing.
- During add, use dynamic max level and probability for node promotion.
- Search and erase remain similar but respect dynamic levels.
💡 This adds complexity but can yield better performance for varying input sizes.
import random
import math
class Node:
def __init__(self, val, level):
self.val = val
self.forward = [None] * (level + 1)
class Skiplist:
def __init__(self):
self.size = 0
self.P = 0.5
self.head = Node(-1, 0)
self.level = 0
def max_level(self):
return max(1, int(math.log2(self.size + 1)))
def random_level(self):
lvl = 0
max_lvl = self.max_level()
while random.random() < self.P and lvl < max_lvl:
lvl += 1
return lvl
def search(self, target: int) -> bool:
curr = self.head
for i in range(self.level, -1, -1):
while curr.forward[i] and curr.forward[i].val < target:
curr = curr.forward[i]
curr = curr.forward[0]
return curr is not None and curr.val == target
def add(self, num: int) -> None:
update = [None] * (self.max_level() + 1)
curr = self.head
for i in range(self.level, -1, -1):
while curr.forward[i] and curr.forward[i].val < num:
curr = curr.forward[i]
update[i] = curr
lvl = self.random_level()
if lvl > self.level:
for i in range(self.level + 1, lvl + 1):
update[i] = self.head
self.level = lvl
new_node = Node(num, lvl)
for i in range(lvl + 1):
new_node.forward[i] = update[i].forward[i]
update[i].forward[i] = new_node
self.size += 1
def erase(self, num: int) -> bool:
update = [None] * (self.max_level() + 1)
curr = self.head
for i in range(self.level, -1, -1):
while curr.forward[i] and curr.forward[i].val < num:
curr = curr.forward[i]
update[i] = curr
curr = curr.forward[0]
if curr is None or curr.val != num:
return False
for i in range(self.level + 1):
if update[i].forward[i] != curr:
continue
update[i].forward[i] = curr.forward[i]
while self.level > 0 and self.head.forward[self.level] is None:
self.level -= 1
self.size -= 1
return True
# Driver code
if __name__ == '__main__':
skiplist = Skiplist()
skiplist.add(1)
skiplist.add(2)
skiplist.add(3)
print(skiplist.search(0)) # False
skiplist.add(4)
print(skiplist.search(1)) # True
print(skiplist.erase(0)) # False
print(skiplist.erase(1)) # True
print(skiplist.search(1)) # False
Line Notes
self.size = 0Track number of elements to compute max level dynamically for adaptive structure.
def max_level(self):Calculate max level based on current size to optimize space and speed.
while random.random() < self.P and lvl < max_lvl:Randomly promote node up to dynamic max level to balance Skiplist.
self.size += 1Increment size after insertion to update max level for future operations.
self.size -= 1Decrement size after deletion to update max level and maintain structure.
import java.util.Random;
class Skiplist {
private int size;
private final double P = 0.5;
private class Node {
int val;
Node[] forward;
Node(int val, int level) {
this.val = val;
forward = new Node[level + 1];
}
}
private Node head;
private int level;
private Random rand;
public Skiplist() {
size = 0;
head = new Node(-1, 0);
level = 0;
rand = new Random();
}
private int maxLevel() {
return Math.max(1, (int)(Math.log(size + 1) / Math.log(2)));
}
private int randomLevel() {
int lvl = 0;
int maxLvl = maxLevel();
while (rand.nextDouble() < P && lvl < maxLvl) {
lvl++;
}
return lvl;
}
public boolean search(int target) {
Node curr = head;
for (int i = level; i >= 0; i--) {
while (curr.forward[i] != null && curr.forward[i].val < target) {
curr = curr.forward[i];
}
}
curr = curr.forward[0];
return curr != null && curr.val == target;
}
public void add(int num) {
Node[] update = new Node[maxLevel() + 1];
Node curr = head;
for (int i = level; i >= 0; i--) {
while (curr.forward[i] != null && curr.forward[i].val < num) {
curr = curr.forward[i];
}
update[i] = curr;
}
int lvl = randomLevel();
if (lvl > level) {
for (int i = level + 1; i <= lvl; i++) {
update[i] = head;
}
level = lvl;
}
Node newNode = new Node(num, lvl);
for (int i = 0; i <= lvl; i++) {
newNode.forward[i] = update[i].forward[i];
update[i].forward[i] = newNode;
}
size++;
}
public boolean erase(int num) {
Node[] update = new Node[maxLevel() + 1];
Node curr = head;
for (int i = level; i >= 0; i--) {
while (curr.forward[i] != null && curr.forward[i].val < num) {
curr = curr.forward[i];
}
update[i] = curr;
}
curr = curr.forward[0];
if (curr == null || curr.val != num) {
return false;
}
for (int i = 0; i <= level; i++) {
if (update[i].forward[i] != curr) continue;
update[i].forward[i] = curr.forward[i];
}
while (level > 0 && head.forward[level] == null) {
level--;
}
size--;
return true;
}
public static void main(String[] args) {
Skiplist skiplist = new Skiplist();
skiplist.add(1);
skiplist.add(2);
skiplist.add(3);
System.out.println(skiplist.search(0)); // false
skiplist.add(4);
System.out.println(skiplist.search(1)); // true
System.out.println(skiplist.erase(0)); // false
System.out.println(skiplist.erase(1)); // true
System.out.println(skiplist.search(1)); // false
}
}
Line Notes
private int size;Track number of elements for dynamic max level calculation.
return Math.max(1, (int)(Math.log(size + 1) / Math.log(2)));Calculate max level based on current size to adapt Skiplist height.
while (rand.nextDouble() < P && lvl < maxLvl)Randomly promote node up to dynamic max level for balancing.
size++;Increment size after insertion to update max level.
size--;Decrement size after deletion to maintain accurate size.
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>
using namespace std;
class Skiplist {
struct Node {
int val;
Node** forward;
Node(int v, int level) {
val = v;
forward = new Node*[level + 1];
for (int i = 0; i <= level; i++) forward[i] = nullptr;
}
~Node() { delete[] forward; }
};
Node* head;
int level;
int size;
static constexpr float P = 0.5;
int maxLevel() {
return max(1, (int)log2(size + 1));
}
int randomLevel() {
int lvl = 0;
int maxLvl = maxLevel();
while (((float) rand() / RAND_MAX) < P && lvl < maxLvl) {
lvl++;
}
return lvl;
}
public:
Skiplist() {
srand(time(nullptr));
size = 0;
level = 0;
head = new Node(-1, 0);
}
bool search(int target) {
Node* curr = head;
for (int i = level; i >= 0; i--) {
while (curr->forward[i] && curr->forward[i]->val < target) {
curr = curr->forward[i];
}
}
curr = curr->forward[0];
return curr && curr->val == target;
}
void add(int num) {
Node* update[64];
Node* curr = head;
for (int i = level; i >= 0; i--) {
while (curr->forward[i] && curr->forward[i]->val < num) {
curr = curr->forward[i];
}
update[i] = curr;
}
int lvl = randomLevel();
if (lvl > level) {
for (int i = level + 1; i <= lvl; i++) {
update[i] = head;
}
level = lvl;
}
Node* newNode = new Node(num, lvl);
for (int i = 0; i <= lvl; i++) {
newNode->forward[i] = update[i]->forward[i];
update[i]->forward[i] = newNode;
}
size++;
}
bool erase(int num) {
Node* update[64];
Node* curr = head;
for (int i = level; i >= 0; i--) {
while (curr->forward[i] && curr->forward[i]->val < num) {
curr = curr->forward[i];
}
update[i] = curr;
}
curr = curr->forward[0];
if (!curr || curr->val != num) return false;
for (int i = 0; i <= level; i++) {
if (update[i]->forward[i] != curr) continue;
update[i]->forward[i] = curr->forward[i];
}
delete curr;
while (level > 0 && head->forward[level] == nullptr) {
level--;
}
size--;
return true;
}
};
int main() {
Skiplist skiplist;
skiplist.add(1);
skiplist.add(2);
skiplist.add(3);
cout << boolalpha << skiplist.search(0) << endl; // false
skiplist.add(4);
cout << skiplist.search(1) << endl; // true
cout << skiplist.erase(0) << endl; // false
cout << skiplist.erase(1) << endl; // true
cout << skiplist.search(1) << endl; // false
return 0;
}
Line Notes
int size;Track number of elements for dynamic max level calculation.
return max(1, (int)log2(size + 1));Calculate max level based on current size to adapt Skiplist height.
while (((float) rand() / RAND_MAX) < P && lvl < maxLvl)Randomly promote node up to dynamic max level for balancing.
size++;Increment size after insertion to update max level.
size--;Decrement size after deletion to maintain accurate size.
class Node {
constructor(val, level) {
this.val = val;
this.forward = new Array(level + 1).fill(null);
}
}
class Skiplist {
constructor() {
this.level = 0;
this.size = 0;
this.P = 0.5;
this.head = new Node(-1, 0);
}
maxLevel() {
return Math.max(1, Math.floor(Math.log2(this.size + 1)));
}
randomLevel() {
let lvl = 0;
let maxLvl = this.maxLevel();
while (Math.random() < this.P && lvl < maxLvl) {
lvl++;
}
return lvl;
}
search(target) {
let curr = this.head;
for (let i = this.level; i >= 0; i--) {
while (curr.forward[i] && curr.forward[i].val < target) {
curr = curr.forward[i];
}
}
curr = curr.forward[0];
return curr !== null && curr.val === target;
}
add(num) {
const update = new Array(this.maxLevel() + 1);
let curr = this.head;
for (let i = this.level; i >= 0; i--) {
while (curr.forward[i] && curr.forward[i].val < num) {
curr = curr.forward[i];
}
update[i] = curr;
}
const lvl = this.randomLevel();
if (lvl > this.level) {
for (let i = this.level + 1; i <= lvl; i++) {
update[i] = this.head;
}
this.level = lvl;
}
const newNode = new Node(num, lvl);
for (let i = 0; i <= lvl; i++) {
newNode.forward[i] = update[i].forward[i];
update[i].forward[i] = newNode;
}
this.size++;
}
erase(num) {
const update = new Array(this.maxLevel() + 1);
let curr = this.head;
for (let i = this.level; i >= 0; i--) {
while (curr.forward[i] && curr.forward[i].val < num) {
curr = curr.forward[i];
}
update[i] = curr;
}
curr = curr.forward[0];
if (!curr || curr.val !== num) return false;
for (let i = 0; i <= this.level; i++) {
if (update[i].forward[i] !== curr) continue;
update[i].forward[i] = curr.forward[i];
}
while (this.level > 0 && this.head.forward[this.level] === null) {
this.level--;
}
this.size--;
return true;
}
}
// Test
const skiplist = new Skiplist();
skiplist.add(1);
skiplist.add(2);
skiplist.add(3);
console.log(skiplist.search(0)); // false
skiplist.add(4);
console.log(skiplist.search(1)); // true
console.log(skiplist.erase(0)); // false
console.log(skiplist.erase(1)); // true
console.log(skiplist.search(1)); // false
Line Notes
this.size = 0;Track number of elements for dynamic max level calculation.
return Math.max(1, Math.floor(Math.log2(this.size + 1)));Calculate max level based on current size to adapt Skiplist height.
while (Math.random() < this.P && lvl < maxLvl)Randomly promote node up to dynamic max level for balancing.
this.size++;Increment size after insertion to update max level.
this.size--;Decrement size after deletion to maintain accurate size.
TimeAverage O(log n) per operation with adaptive max level
SpaceO(n) with dynamic pointer arrays
Dynamic max level adapts to size, potentially improving space and time efficiency for large inputs.
💡 For n=10000, max level ~14, so operations traverse fewer nodes than fixed small max level.
Interview Verdict: Accepted and adaptive for large scale
This approach is more complex but can yield better performance and memory usage in practice.