Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
node_modules
.venv
__pycache__
venv
94 changes: 94 additions & 0 deletions Sprint-2/implement_skip_list/skip_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import random

class SkipListNode:
def __init__(self, value, level):
self.value = value
self.next = [None] * (level + 1) # Next nodes at each level

class SkipList:
def __init__(self):
self.max_level = 16
self.head = SkipListNode(None, self.max_level) # Head node
self.level = 0 # Current highest level
self.size = 0

def _random_level(self):
"""Generate random level for a new node (coin flip)"""
level = 0
while random.random() < 0.5 and level < self.max_level:
level += 1
return level

def __contains__(self, key):
"""Support 'in' operator"""
return self.contains(key)

def contains(self, key):
"""Check if key exists in the skip list"""
current = self.head

# Start from highest level and move down
for i in range(self.level, -1, -1):
while current.next[i] and current.next[i].value < key:
current = current.next[i]

# Move to the node that might contain the value
current = current.next[0]
return current is not None and current.value == key

def insert(self, value):
"""Insert a value into the skip list"""
update = [None] * (self.max_level + 1)
current = self.head

# Find the position to insert and track update pointers
for i in range(self.level, -1, -1):
while current.next[i] and current.next[i].value < value:
current = current.next[i]
update[i] = current

current = current.next[0]

# If value already exists, we'll still insert it (allow duplicates)
new_level = self._random_level()

# Update the max level if needed
if new_level > self.level:
for i in range(self.level + 1, new_level + 1):
update[i] = self.head
self.level = new_level

# Create new node
new_node = SkipListNode(value, new_level)

# Update pointers at each level
for i in range(new_level + 1):
new_node.next[i] = update[i].next[i]
update[i].next[i] = new_node

self.size += 1

def to_list(self):
"""Convert skip list to a sorted list"""
result = []
current = self.head.next[0] # Start from first element at level 0

while current:
result.append(current.value)
current = current.next[0]

return result

def __str__(self):
"""String representation for debugging"""
result = []
for level in range(self.level, -1, -1):
level_str = f"Level {level}: "
current = self.head.next[level]
elements = []
while current:
elements.append(str(current.value))
current = current.next[level]
level_str += " -> ".join(elements) if elements else "empty"
result.append(level_str)
return "\n".join(result)
45 changes: 45 additions & 0 deletions Sprint-2/implement_skip_list/skip_list_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,51 @@ def test_general_usage(self):

self.assertEqual(sl.to_list(), [1, 2, 3, 4, 5, 10])

def test_duplicate_values(self):
"""Test that duplicate values are handled correctly"""
sl = SkipList()

# Insert duplicates
sl.insert(5)
sl.insert(2)
sl.insert(5)
sl.insert(2)
sl.insert(5)

# All duplicates should be in the list and sorted
self.assertEqual(sl.to_list(), [2, 2, 5, 5, 5])

# Check membership for duplicates
self.assertIn(2, sl)
self.assertIn(5, sl)

# Verify non-existent values
self.assertNotIn(1, sl)
self.assertNotIn(3, sl)
self.assertNotIn(6, sl)

def test_large_random_inserts(self):
"""Test with larger dataset and random insertion order"""
sl = SkipList()

# Insert numbers in random order
numbers = [7, 3, 9, 1, 8, 2, 6, 4, 10, 5]
for num in numbers:
sl.insert(num)

# Should be sorted regardless of insertion order
self.assertEqual(sl.to_list(), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])

# Verify all numbers are found
for num in range(1, 11):
self.assertIn(num, sl)

# Verify numbers outside range are not found
self.assertNotIn(0, sl)
self.assertNotIn(11, sl)
self.assertNotIn(15, sl)



if __name__ == "__main__":
unittest.main()