Skip to content

Commit e3586fa

Browse files
committed
Merge branch 'master' into Allgood
2 parents b315c5e + bcf9753 commit e3586fa

File tree

9 files changed

+296
-25
lines changed

9 files changed

+296
-25
lines changed

pydatastructs/linear_data_structures/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
SinglyLinkedList,
1818
DoublyLinkedList,
1919
SinglyCircularLinkedList,
20-
DoublyCircularLinkedList
20+
DoublyCircularLinkedList,
21+
SkipList
2122
)
2223
__all__.extend(linked_lists.__all__)
2324

pydatastructs/linear_data_structures/linked_lists.py

Lines changed: 199 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
from pydatastructs.utils.misc_util import _check_type, LinkedListNode
1+
from pydatastructs.utils.misc_util import _check_type, LinkedListNode, SkipNode
2+
import math, random
23

34
__all__ = [
45
'SinglyLinkedList',
56
'DoublyLinkedList',
67
'SinglyCircularLinkedList',
7-
'DoublyCircularLinkedList'
8+
'DoublyCircularLinkedList',
9+
'SkipList'
810
]
911

1012
class LinkedList(object):
@@ -219,12 +221,12 @@ class DoublyLinkedList(LinkedList):
219221
>>> dll.append(5)
220222
>>> dll.appendleft(2)
221223
>>> str(dll)
222-
"['2', '6', '5']"
224+
"['(2, None)', '(6, None)', '(5, None)']"
223225
>>> dll[0].key = 7.2
224226
>>> dll.extract(1).key
225227
6
226228
>>> str(dll)
227-
"['7.2', '5']"
229+
"['(7.2, None)', '(5, None)']"
228230
229231
References
230232
==========
@@ -290,6 +292,11 @@ def insert_at(self, index, key, data=None):
290292
if self.size == 1:
291293
self.head, self.tail = \
292294
new_node, new_node
295+
elif index == self.size - 1:
296+
new_node.prev = self.tail
297+
new_node.next = self.tail.next
298+
self.tail.next = new_node
299+
self.tail = new_node
293300
else:
294301
counter = 0
295302
current_node = self.head
@@ -354,12 +361,12 @@ class SinglyLinkedList(LinkedList):
354361
>>> sll.append(5)
355362
>>> sll.appendleft(2)
356363
>>> str(sll)
357-
"['2', '6', '5']"
364+
"['(2, None)', '(6, None)', '(5, None)']"
358365
>>> sll[0].key = 7.2
359366
>>> sll.extract(1).key
360367
6
361368
>>> str(sll)
362-
"['7.2', '5']"
369+
"['(7.2, None)', '(5, None)']"
363370
364371
References
365372
==========
@@ -409,6 +416,10 @@ def insert_at(self, index, key, data=None):
409416
if self.size == 1:
410417
self.head, self.tail = \
411418
new_node, new_node
419+
elif index == self.size - 1:
420+
new_node.next = self.tail.next
421+
self.tail.next = new_node
422+
self.tail = new_node
412423
else:
413424
counter = 0
414425
current_node = self.head
@@ -469,12 +480,12 @@ class SinglyCircularLinkedList(SinglyLinkedList):
469480
>>> scll.append(5)
470481
>>> scll.appendleft(2)
471482
>>> str(scll)
472-
"['2', '6', '5']"
483+
"['(2, None)', '(6, None)', '(5, None)']"
473484
>>> scll[0].key = 7.2
474485
>>> scll.extract(1).key
475486
6
476487
>>> str(scll)
477-
"['7.2', '5']"
488+
"['(7.2, None)', '(5, None)']"
478489
479490
References
480491
==========
@@ -528,12 +539,12 @@ class DoublyCircularLinkedList(DoublyLinkedList):
528539
>>> dcll.append(5)
529540
>>> dcll.appendleft(2)
530541
>>> str(dcll)
531-
"['2', '6', '5']"
542+
"['(2, None)', '(6, None)', '(5, None)']"
532543
>>> dcll[0].key = 7.2
533544
>>> dcll.extract(1).key
534545
6
535546
>>> str(dcll)
536-
"['7.2', '5']"
547+
"['(7.2, None)', '(5, None)']"
537548
538549
References
539550
==========
@@ -581,3 +592,181 @@ def extract(self, index):
581592
elif index == 0:
582593
self.tail.next = self.head
583594
return node
595+
596+
class SkipList(object):
597+
"""
598+
Represents Skip List
599+
600+
Examples
601+
========
602+
603+
>>> from pydatastructs import SkipList
604+
>>> sl = SkipList()
605+
>>> sl.insert(6)
606+
>>> sl.insert(1)
607+
>>> sl.insert(3)
608+
>>> node = sl.extract(1)
609+
>>> str(node)
610+
'(1, None)'
611+
>>> sl.insert(4)
612+
>>> sl.insert(2)
613+
>>> sl.search(4)
614+
True
615+
>>> sl.search(10)
616+
False
617+
618+
"""
619+
620+
__slots__ = ['head', 'tail', '_levels', '_num_nodes', 'seed']
621+
622+
def __new__(cls):
623+
obj = object.__new__(cls)
624+
obj.head, obj.tail = None, None
625+
obj._num_nodes = 0
626+
obj._levels = 0
627+
obj._add_level()
628+
return obj
629+
630+
@classmethod
631+
def methods(cls):
632+
return ['__new__', 'levels', 'search',
633+
'extract', '__str__', 'size']
634+
635+
def _add_level(self):
636+
self.tail = SkipNode(math.inf, next=None, down=self.tail)
637+
self.head = SkipNode(-math.inf, next=self.tail, down=self.head)
638+
self._levels += 1
639+
640+
@property
641+
def levels(self):
642+
"""
643+
Returns the number of levels in the
644+
current skip list.
645+
"""
646+
return self._levels
647+
648+
def _search(self, key) -> list:
649+
path = []
650+
node = self.head
651+
while node:
652+
if node.next.key >= key:
653+
path.append(node)
654+
node = node.down
655+
else:
656+
node = node.next
657+
return path
658+
659+
def search(self, key) -> bool:
660+
return self._search(key)[-1].next.key == key
661+
662+
def insert(self, key, data=None):
663+
"""
664+
Inserts a new node to the skip list.
665+
666+
Parameters
667+
==========
668+
669+
key
670+
Any valid identifier to uniquely
671+
identify the node in the linked list.
672+
673+
data
674+
Any valid data to be stored in the node.
675+
"""
676+
path = self._search(key)
677+
tip = path[-1]
678+
below = SkipNode(key=key, data=data, next=tip.next)
679+
tip.next = below
680+
total_level = self._levels
681+
level = 1
682+
while random.getrandbits(1) % 2 == 0 and level <= total_level:
683+
if level == total_level:
684+
self._add_level()
685+
prev = self.head
686+
else:
687+
prev = path[total_level - 1 - level]
688+
below = SkipNode(key=key, data=None, next=prev.next, down=below)
689+
prev.next = below
690+
level += 1
691+
self._num_nodes += 1
692+
693+
@property
694+
def size(self):
695+
return self._num_nodes
696+
697+
def extract(self, key):
698+
"""
699+
Extracts the node with the given key in the skip list.
700+
701+
Parameters
702+
==========
703+
704+
key
705+
The key of the node under consideration.
706+
707+
Returns
708+
=======
709+
710+
return_node: SkipNode
711+
The node with given key.
712+
"""
713+
path = self._search(key)
714+
tip = path[-1]
715+
if tip.next.key != key:
716+
raise KeyError('Node with key %s is not there in %s'%(key, self))
717+
return_node = SkipNode(tip.next.key, tip.next.data)
718+
total_level = self._levels
719+
level = total_level - 1
720+
while level >= 0 and path[level].next.key == key:
721+
path[level].next = path[level].next.next
722+
level -= 1
723+
walk = self.head
724+
while walk is not None:
725+
if walk.next is self.tail:
726+
self._levels -= 1
727+
self.head = walk.down
728+
self.tail = self.tail.down
729+
walk = walk.down
730+
else:
731+
break
732+
self._num_nodes -= 1
733+
if self._levels == 0:
734+
self._add_level()
735+
return return_node
736+
737+
def __str__(self):
738+
node2row = {}
739+
node2col = {}
740+
walk = self.head
741+
curr_level = self._levels - 1
742+
while walk is not None:
743+
curr_node = walk
744+
col = 0
745+
while curr_node is not None:
746+
if curr_node.key != math.inf and curr_node.key != -math.inf:
747+
node2row[curr_node] = curr_level
748+
if walk.down is None:
749+
node2col[curr_node.key] = col
750+
col += 1
751+
curr_node = curr_node.next
752+
walk = walk.down
753+
curr_level -= 1
754+
print(self._num_nodes, self._levels)
755+
sl_mat = [[str(None) for _ in range(self._num_nodes)] for _ in range(self._levels)]
756+
walk = self.head
757+
while walk is not None:
758+
curr_node = walk
759+
while curr_node is not None:
760+
if curr_node in node2row:
761+
row = node2row[curr_node]
762+
col = node2col[curr_node.key]
763+
sl_mat[row][col] = str(curr_node)
764+
curr_node = curr_node.next
765+
walk = walk.down
766+
sl_str = ""
767+
for level_list in sl_mat[::-1]:
768+
for node_str in level_list:
769+
sl_str += node_str + " "
770+
if len(sl_str) > 0:
771+
sl_str += "\n"
772+
return sl_str

pydatastructs/linear_data_structures/tests/test_linked_lists.py

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from pydatastructs.linear_data_structures import DoublyLinkedList, SinglyLinkedList, SinglyCircularLinkedList, DoublyCircularLinkedList
1+
from pydatastructs.linear_data_structures import DoublyLinkedList, SinglyLinkedList, SinglyCircularLinkedList, DoublyCircularLinkedList, SkipList
22
from pydatastructs.utils.raises_util import raises
33
import copy, random
44

@@ -22,7 +22,9 @@ def test_DoublyLinkedList():
2222
assert dll.search(3) == dll[-2]
2323
assert dll.search(-1) is None
2424
dll[-2].key = 0
25-
assert str(dll) == "['7', '5', '1', '6', '1.1', '0', '9']"
25+
assert str(dll) == ("['(7, None)', '(5, None)', '(1, None)', "
26+
"'(6, None)', '(1.1, None)', '(0, None)', "
27+
"'(9, None)']")
2628
assert len(dll) == 7
2729
assert raises(IndexError, lambda: dll.insert_at(8, None))
2830
assert raises(IndexError, lambda: dll.extract(20))
@@ -57,7 +59,8 @@ def test_SinglyLinkedList():
5759
assert sll.popleft().key == 2
5860
assert sll.popright().key == 6
5961
sll[-2].key = 0
60-
assert str(sll) == "['2', '4', '1', '0', '9']"
62+
assert str(sll) == ("['(2, None)', '(4, None)', '(1, None)', "
63+
"'(0, None)', '(9, None)']")
6164
assert len(sll) == 5
6265
assert raises(IndexError, lambda: sll.insert_at(6, None))
6366
assert raises(IndexError, lambda: sll.extract(20))
@@ -93,7 +96,8 @@ def test_SinglyCircularLinkedList():
9396
assert scll.popright().key == 6
9497
assert scll.search(-1) is None
9598
scll[-2].key = 0
96-
assert str(scll) == "['2', '4', '1', '0', '9']"
99+
assert str(scll) == ("['(2, None)', '(4, None)', '(1, None)', "
100+
"'(0, None)', '(9, None)']")
97101
assert len(scll) == 5
98102
assert raises(IndexError, lambda: scll.insert_at(6, None))
99103
assert raises(IndexError, lambda: scll.extract(20))
@@ -130,7 +134,9 @@ def test_DoublyCircularLinkedList():
130134
assert dcll.popleft().key == 2
131135
assert dcll.popright().key == 4
132136
dcll[-2].key = 0
133-
assert str(dcll) == "['7', '5', '1', '6', '1', '0', '9']"
137+
assert str(dcll) == ("['(7, None)', '(5, None)', '(1, None)', "
138+
"'(6, None)', '(1, None)', '(0, None)', "
139+
"'(9, None)']")
134140
assert len(dcll) == 7
135141
assert raises(IndexError, lambda: dcll.insert_at(8, None))
136142
assert raises(IndexError, lambda: dcll.extract(20))
@@ -148,3 +154,40 @@ def test_DoublyCircularLinkedList():
148154
dcll_copy.extract(index)
149155
assert str(dcll_copy) == "[]"
150156
assert raises(ValueError, lambda: dcll_copy.extract(1))
157+
158+
def test_SkipList():
159+
random.seed(0)
160+
sl = SkipList()
161+
sl.insert(2)
162+
sl.insert(10)
163+
sl.insert(92)
164+
sl.insert(1)
165+
sl.insert(4)
166+
sl.insert(27)
167+
sl.extract(10)
168+
assert str(sl) == ("(1, None) None None None None \n"
169+
"(1, None) None None None None \n"
170+
"(1, None) (2, None) (4, None) (27, None) (92, None) \n")
171+
assert raises(KeyError, lambda: sl.extract(15))
172+
assert sl.search(1) is True
173+
assert sl.search(47) is False
174+
175+
sl = SkipList()
176+
177+
for a in range(0, 20, 2):
178+
sl.insert(a)
179+
assert sl.search(16) is True
180+
for a in range(4, 20, 4):
181+
sl.extract(a)
182+
assert sl.search(10) is True
183+
for a in range(4, 20, 4):
184+
sl.insert(a)
185+
for a in range(0, 20, 2):
186+
sl.extract(a)
187+
assert sl.search(3) is False
188+
189+
li = SkipList()
190+
li.insert(1)
191+
li.insert(2)
192+
assert li.levels == 1
193+
assert li.size == 2

pydatastructs/miscellaneous_data_structures/tests/test_queue.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ def test_LinkedListQueue():
5454
q1 = Queue(implementation='linked_list', items = [0, 1])
5555
q1.append(2)
5656
q1.append(3)
57-
assert str(q1) == "['0', '1', '2', '3']"
57+
assert str(q1) == ("['(0, None)', '(1, None)', "
58+
"'(2, None)', '(3, None)']")
5859
assert len(q1) == 4
5960
assert q1.popleft().key == 0
6061
assert q1.popleft().key == 1
@@ -83,7 +84,7 @@ def test_LinkedListQueue():
8384
q1 = Queue(implementation='linked_list', items = [0, 1], double_ended=True)
8485
q1.appendleft(2)
8586
q1.append(3)
86-
assert str(q1) == "['2', '0', '1', '3']"
87+
assert str(q1) == "['(2, None)', '(0, None)', '(1, None)', '(3, None)']"
8788
assert len(q1) == 4
8889
assert q1.popleft().key == 2
8990
assert q1.pop().key == 3

0 commit comments

Comments
 (0)