Skip Lists: Concurrent Data Structures for Interviews
Skip lists are probabilistic data structures that provide O(log n) operations like balanced trees but with simpler implementation and better concurrent access properties.
How Skip Lists Work
A skip list has multiple layers of linked lists. The bottom layer contains all elements. Each higher layer acts as an "express lane," containing a subset of elements.
Layer 3: HEAD -------------------------> 50 -------------------------> NIL
Layer 2: HEAD ---------> 25 ---------> 50 ---------> 75 ---------> NIL
Layer 1: HEAD -> 10 -> 25 -> 30 -> 50 -> 60 -> 75 -> 80 -> 90 -> NIL
Basic Implementation
import random
class SkipNode:
def __init__(self, value=None, levels=0):
self.value = value
self.forward = [None] * levels
class SkipList:
def __init__(self, max_levels=16, p=0.5):
self.max_levels = max_levels
self.p = p
self.level = 1
self.head = SkipNode(levels=max_levels)
def random_level(self):
level = 1
while random.random() < self.p and level < self.max_levels:
level += 1
return level
def search(self, value):
current = self.head
for i in range(self.level - 1, -1, -1):
while current.forward[i] and current.forward[i].value < value:
current = current.forward[i]
current = current.forward[0]
return current if current and current.value == value else None
def insert(self, value):
update = [None] * self.max_levels
current = self.head
for i in range(self.level - 1, -1, -1):
while current.forward[i] and current.forward[i].value < value:
current = current.forward[i]
update[i] = current
new_level = self.random_level()
if new_level > self.level:
for i in range(self.level, new_level):
update[i] = self.head
self.level = new_level
new_node = SkipNode(value, new_level)
for i in range(new_level):
new_node.forward[i] = update[i].forward[i]
update[i].forward[i] = new_node
def delete(self, value):
update = [None] * self.max_levels
current = self.head
for i in range(self.level - 1, -1, -1):
while current.forward[i] and current.forward[i].value < value:
current = current.forward[i]
update[i] = current
current = current.forward[0]
if current and current.value == value:
for i in range(self.level):
if update[i].forward[i] != current:
break
update[i].forward[i] = current.forward[i]
while self.level > 1 and not self.head.forward[self.level - 1]:
self.level -= 1
Concurrent Skip List
The key advantage of skip lists is lock-free or fine-grained locking for concurrent access:
import threading
class ConcurrentSkipList:
def __init__(self, max_levels=16):
self.max_levels = max_levels
self.level = 1
self.head = SkipNode(levels=max_levels)
self.locks = [threading.Lock() for _ in range(max_levels)]
def search(self, value):
current = self.head
for i in range(self.level - 1, -1, -1):
while current.forward[i] and current.forward[i].value < value:
current = current.forward[i]
current = current.forward[0]
return current if current and current.value == value else None
def insert(self, value):
update = [None] * self.max_levels
current = self.head
# Find positions (read-only, no locks needed)
for i in range(self.level - 1, -1, -1):
while current.forward[i] and current.forward[i].value < value:
current = current.forward[i]
update[i] = current
new_level = self.random_level()
# Lock affected levels
for i in range(new_level):
self.locks[i].acquire()
try:
new_node = SkipNode(value, new_level)
for i in range(new_level):
new_node.forward[i] = update[i].forward[i]
update[i].forward[i] = new_node
if new_level > self.level:
self.level = new_level
finally:
for i in range(new_level):
self.locks[i].release()
Skip Lists vs Balanced Trees
| Property | Skip List | Red-Black Tree |
|----------|-----------|----------------|
| Implementation | Simple | Complex |
| Concurrency | Easy to parallelize | Rebalancing complicates locking |
| Memory | More pointers | Less overhead |
| Deterministic | Probabilistic O(log n) | Guaranteed O(log n) |
Interview Tips
- Explain the probability. Each level contains roughly half the elements of the level below, giving O(log n) expected search.
- Concurrency angle. Skip lists are used in Java's ConcurrentSkipListMap, Redis for sorted sets.
- Common follow-ups: "Compare with B-trees," "Implement range queries," "Handle duplicates."
- Real applications. LevelDB uses skip lists for memtables, Apache Lucene for term dictionaries.
Time Complexity
All operations (search, insert, delete): O(log n) expected, O(n) worst case
Space Complexity: O(n) expected
Ready to practice for your next interview? Interview Simulator gives you AI-powered feedback on your responses — behavioral, technical, and system design. Start with 3 free practice interviews today.
Try Interview Simulator free →
Master the most common coding interview patterns to ace your technical rounds.
For technical interview preparation, our system design guide is essential reading.
Don't neglect behavioral preparation — see our behavioral interview guide.
Your resume is the first impression — get it right with our resume guide.
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Skip Lists: Concurrent Data Structures for Interviews",
"description": "Master skip lists—the probabilistic alternative to balanced trees with excellent concurrent access properties.",
"datePublished": "2026-03-21",
"author": {
"@type": "Organization",
"name": "CodeSwiftr Team"
},
"url": "https://codeswiftr.com/blog/skip-lists-concurrent-ds"
}
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "How should I structure my technical interview preparation?",
"acceptedAnswer": {
"@type": "Answer",
"text": "A structured 8–12 week preparation plan is most effective. Week 1–4: data structures and algorithms fundamentals using LeetCode easy-to-medium problems. Week 5–7: system design patterns using resources like the System Design Primer. Week 8–10: behavioral interview preparation with the STAR method. Week 11–12: company-specific mock interviews and review. Consistency matters more than intensity — 1–2 hours per day beats irregular all-day sessions."
}
},
{
"@type": "Question",
"name": "What is the STAR method and how should I use it in interviews?",
"acceptedAnswer": {
"@type": "Answer",
"text": "STAR stands for Situation, Task, Action, Result. It is the standard framework for answering behavioral interview questions. Situation: briefly describe the context (1–2 sentences). Task: explain your specific responsibility. Action: detail the steps you personally took — use 'I' not 'we'. Result: quantify the outcome wherever possible (e.g., 'reduced latency by 40%', 'increased conversion rate by 12%'). Keep each answer to 2–3 minutes."
}
},
{
"@type": "Question",
"name": "What LeetCode difficulty level should I focus on?",
"acceptedAnswer": {
"@type": "Answer",
"text": "For FAANG interviews, focus 20% on easy, 60% on medium, and 20% on hard problems. Easy problems build speed and confidence. Medium problems represent the most common interview difficulty at top companies. Hard problems appear mostly at Google, Meta, and specialised algorithmic roles. Do not skip easy problems — many candidates fail by overthinking genuinely simple questions that require clean, efficient solutions."
}
},
{
"@type": "Question",
"name": "How important is system design compared to coding in tech interviews?",
"acceptedAnswer": {
"@type": "Answer",
"text": "At mid-level and senior positions, system design carries equal or greater weight than coding. Entry-level and new-grad interviews are predominantly coding-focused. Senior and staff-level interviews dedicate one or two full rounds to system design. Master the core components: load balancers, databases (SQL vs NoSQL trade-offs), caches (Redis, Memcached), message queues (Kafka, SQS), CDNs, and API design. Practice designing real systems you use daily — the interviewer values practical reasoning over textbook answers."
}
}
]
}
Explore Related Topics
- Data Structures & Algorithms Cheat Sheet: Time...
- Data Structures Interview Cheatsheet: Complexity, Use...
- Data Structures Interview Review: Arrays, Trees, Graphs,...