|
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 |
2 | 3 |
|
3 | 4 | __all__ = [
|
4 | 5 | 'SinglyLinkedList',
|
5 | 6 | 'DoublyLinkedList',
|
6 | 7 | 'SinglyCircularLinkedList',
|
7 |
| - 'DoublyCircularLinkedList' |
| 8 | + 'DoublyCircularLinkedList', |
| 9 | + 'SkipList' |
8 | 10 | ]
|
9 | 11 |
|
10 | 12 | class LinkedList(object):
|
@@ -219,12 +221,12 @@ class DoublyLinkedList(LinkedList):
|
219 | 221 | >>> dll.append(5)
|
220 | 222 | >>> dll.appendleft(2)
|
221 | 223 | >>> str(dll)
|
222 |
| - "['2', '6', '5']" |
| 224 | + "['(2, None)', '(6, None)', '(5, None)']" |
223 | 225 | >>> dll[0].key = 7.2
|
224 | 226 | >>> dll.extract(1).key
|
225 | 227 | 6
|
226 | 228 | >>> str(dll)
|
227 |
| - "['7.2', '5']" |
| 229 | + "['(7.2, None)', '(5, None)']" |
228 | 230 |
|
229 | 231 | References
|
230 | 232 | ==========
|
@@ -290,6 +292,11 @@ def insert_at(self, index, key, data=None):
|
290 | 292 | if self.size == 1:
|
291 | 293 | self.head, self.tail = \
|
292 | 294 | 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 |
293 | 300 | else:
|
294 | 301 | counter = 0
|
295 | 302 | current_node = self.head
|
@@ -354,12 +361,12 @@ class SinglyLinkedList(LinkedList):
|
354 | 361 | >>> sll.append(5)
|
355 | 362 | >>> sll.appendleft(2)
|
356 | 363 | >>> str(sll)
|
357 |
| - "['2', '6', '5']" |
| 364 | + "['(2, None)', '(6, None)', '(5, None)']" |
358 | 365 | >>> sll[0].key = 7.2
|
359 | 366 | >>> sll.extract(1).key
|
360 | 367 | 6
|
361 | 368 | >>> str(sll)
|
362 |
| - "['7.2', '5']" |
| 369 | + "['(7.2, None)', '(5, None)']" |
363 | 370 |
|
364 | 371 | References
|
365 | 372 | ==========
|
@@ -409,6 +416,10 @@ def insert_at(self, index, key, data=None):
|
409 | 416 | if self.size == 1:
|
410 | 417 | self.head, self.tail = \
|
411 | 418 | 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 |
412 | 423 | else:
|
413 | 424 | counter = 0
|
414 | 425 | current_node = self.head
|
@@ -469,12 +480,12 @@ class SinglyCircularLinkedList(SinglyLinkedList):
|
469 | 480 | >>> scll.append(5)
|
470 | 481 | >>> scll.appendleft(2)
|
471 | 482 | >>> str(scll)
|
472 |
| - "['2', '6', '5']" |
| 483 | + "['(2, None)', '(6, None)', '(5, None)']" |
473 | 484 | >>> scll[0].key = 7.2
|
474 | 485 | >>> scll.extract(1).key
|
475 | 486 | 6
|
476 | 487 | >>> str(scll)
|
477 |
| - "['7.2', '5']" |
| 488 | + "['(7.2, None)', '(5, None)']" |
478 | 489 |
|
479 | 490 | References
|
480 | 491 | ==========
|
@@ -528,12 +539,12 @@ class DoublyCircularLinkedList(DoublyLinkedList):
|
528 | 539 | >>> dcll.append(5)
|
529 | 540 | >>> dcll.appendleft(2)
|
530 | 541 | >>> str(dcll)
|
531 |
| - "['2', '6', '5']" |
| 542 | + "['(2, None)', '(6, None)', '(5, None)']" |
532 | 543 | >>> dcll[0].key = 7.2
|
533 | 544 | >>> dcll.extract(1).key
|
534 | 545 | 6
|
535 | 546 | >>> str(dcll)
|
536 |
| - "['7.2', '5']" |
| 547 | + "['(7.2, None)', '(5, None)']" |
537 | 548 |
|
538 | 549 | References
|
539 | 550 | ==========
|
@@ -581,3 +592,181 @@ def extract(self, index):
|
581 | 592 | elif index == 0:
|
582 | 593 | self.tail.next = self.head
|
583 | 594 | 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 |
0 commit comments