diff --git a/pydatastructs/linear_data_structures/__init__.py b/pydatastructs/linear_data_structures/__init__.py index 8a67a1537..a6e0d74b1 100644 --- a/pydatastructs/linear_data_structures/__init__.py +++ b/pydatastructs/linear_data_structures/__init__.py @@ -13,6 +13,8 @@ from .linked_lists import ( SinglyLinkedList, - DoublyLinkedList + DoublyLinkedList, + SinglyCircularLinkedList, + DoublyCircularLinkedList ) __all__.extend(linked_lists.__all__) diff --git a/pydatastructs/linear_data_structures/linked_lists.py b/pydatastructs/linear_data_structures/linked_lists.py index 0fc0cd21d..49159b3bf 100644 --- a/pydatastructs/linear_data_structures/linked_lists.py +++ b/pydatastructs/linear_data_structures/linked_lists.py @@ -2,7 +2,9 @@ __all__ = [ 'SinglyLinkedList', - 'DoublyLinkedList' + 'DoublyLinkedList', + 'SinglyCircularLinkedList', + 'DoublyCircularLinkedList' ] class LinkedList(object): @@ -27,6 +29,8 @@ def __str__(self): while current_node is not None: elements.append(current_node.data) current_node = current_node.next + if current_node == self.head: + break return str(elements) class DoublyLinkedList(LinkedList): @@ -508,3 +512,213 @@ def __getitem__(self, index): current_node = current_node.next counter += 1 return current_node + +class SinglyCircularLinkedList(SinglyLinkedList): + """ + Represents Singly Circular Linked List. + + + Examples + ======== + + >>> from pydatastructs import SinglyCircularLinkedList + >>> scll = SinglyCircularLinkedList() + >>> scll.append(6) + >>> scll[0].data + 6 + >>> scll.head.data + 6 + >>> scll.append(5) + >>> scll.append_left(2) + >>> print(scll) + [2, 6, 5] + >>> scll[0].data = 7.2 + >>> scll.extract(1).data + 6 + >>> print(scll) + [7.2, 5] + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Linked_list#Circular_linked_list + + """ + + def insert_after(self, prev_node, data): + """ + Inserts a new node after the prev_node. + + Parameters + ========== + + prev_node: LinkedListNode + The node after which the + new node is to be inserted. + + data + Any valid data to be stored in the node. + """ + super(SinglyCircularLinkedList, self).insert_after(prev_node, data) + if prev_node.next.next == self.head: + self.tail = prev_node.next + + def insert_at(self, index, data): + """ + Inserts a new node at the input index. + + Parameters + ========== + + index: int + An integer satisfying python indexing properties. + + data + Any valid data to be stored in the node. + """ + super(SinglyCircularLinkedList, self).insert_at(index, data) + if self.size == 1: + self.head.next = self.head + new_node = self.__getitem__(index) + if index == 0: + self.tail.next = new_node + if new_node.next == self.head: + self.tail = new_node + + def extract(self, index): + """ + Extracts the node at the index of the list. + + Parameters + ========== + + index: int + An integer satisfying python indexing properties. + + Returns + ======= + + current_node: LinkedListNode + The node at index i. + """ + node = super(SinglyCircularLinkedList, self).extract(index) + if self.tail is None: + self.head = None + elif index == 0: + self.tail.next = self.head + return node + +class DoublyCircularLinkedList(DoublyLinkedList): + """ + Represents Doubly Circular Linked List + + Examples + ======== + + >>> from pydatastructs import DoublyCircularLinkedList + >>> dcll = DoublyCircularLinkedList() + >>> dcll.append(6) + >>> dcll[0].data + 6 + >>> dcll.head.data + 6 + >>> dcll.append(5) + >>> dcll.append_left(2) + >>> print(dcll) + [2, 6, 5] + >>> dcll[0].data = 7.2 + >>> dcll.extract(1).data + 6 + >>> print(dcll) + [7.2, 5] + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Doubly_linked_list#Circular_doubly_linked_lists + + """ + def insert_after(self, prev_node, data): + """ + Inserts a new node after the prev_node. + + Parameters + ========== + + prev_node: LinkedListNode + The node after which the + new node is to be inserted. + + data + Any valid data to be stored in the node. + """ + super(DoublyCircularLinkedList, self).insert_after(prev_node, data) + if prev_node.next.next == self.head: + self.tail = prev_node.next + + def insert_before(self, next_node, data): + """ + Inserts a new node before the next_node. + + Parameters + ========== + + next_node: LinkedListNode + The node before which the + new node is to be inserted. + + data + Any valid data to be stored in the node. + """ + super(DoublyCircularLinkedList, self).insert_before(next_node,data) + if next_node == self.head: + self.head = next_node.prev + + def insert_at(self, index, data): + """ + Inserts a new node at the input index. + + Parameters + ========== + + index: int + An integer satisfying python indexing properties. + + data + Any valid data to be stored in the node. + """ + super(DoublyCircularLinkedList, self).insert_at(index, data) + if self.size == 1: + self.head.next = self.head + self.head.prev = self.head + new_node = self.__getitem__(index) + if index == 0: + self.tail.next = new_node + new_node.prev = self.tail + if new_node.next == self.head: + self.tail = new_node + new_node.next = self.head + self.head.prev = new_node + + def extract(self, index): + """ + Extracts the node at the index of the list. + + Parameters + ========== + + index: int + An integer satisfying python indexing properties. + + Returns + ======= + + current_node: LinkedListNode + The node at index i. + """ + node = super(DoublyCircularLinkedList, self).extract(index) + if self.tail is None: + self.head = None + elif index == 0: + self.tail.next = self.head + return node diff --git a/pydatastructs/linear_data_structures/tests/test_linked_lists.py b/pydatastructs/linear_data_structures/tests/test_linked_lists.py index 0087031f4..a05bb338c 100644 --- a/pydatastructs/linear_data_structures/tests/test_linked_lists.py +++ b/pydatastructs/linear_data_structures/tests/test_linked_lists.py @@ -1,4 +1,4 @@ -from pydatastructs.linear_data_structures import DoublyLinkedList, SinglyLinkedList +from pydatastructs.linear_data_structures import DoublyLinkedList, SinglyLinkedList, SinglyCircularLinkedList, DoublyCircularLinkedList from pydatastructs.utils.raises_util import raises import copy, random @@ -69,3 +69,71 @@ def test_SinglyLinkedList(): sll_copy.extract(index) assert str(sll_copy) == "[]" assert raises(ValueError, lambda: sll_copy.extract(1)) + +def test_SinglyCircularLinkedList(): + random.seed(1000) + scll = SinglyCircularLinkedList() + assert raises(IndexError, lambda: scll[2]) + scll.append_left(5) + scll.append(1) + scll.append_left(2) + scll.append(3) + scll.insert_after(scll[1], 4) + scll.insert_after(scll[-1], 6) + scll.insert_at(0, 2) + scll.insert_at(-1, 9) + scll.extract(2) + scll.extract(0) + scll.extract(-1) + scll[-2].data = 0 + assert str(scll) == "[2, 4, 1, 0, 9]" + assert len(scll) == 5 + assert raises(IndexError, lambda: scll.insert_at(6, None)) + assert raises(IndexError, lambda: scll.extract(20)) + scll_copy = copy.deepcopy(scll) + for i in range(len(scll)): + if i%2 == 0: + scll.pop_left() + else: + scll.pop_right() + assert str(scll) == "[]" + for _ in range(len(scll_copy)): + index = random.randint(0, len(scll_copy) - 1) + scll_copy.extract(index) + assert str(scll_copy) == "[]" + assert raises(ValueError, lambda: scll_copy.extract(1)) + +def test_DoublyCircularLinkedList(): + random.seed(1000) + dcll = DoublyCircularLinkedList() + assert raises(IndexError, lambda: dcll[2]) + dcll.append_left(5) + dcll.append(1) + dcll.append_left(2) + dcll.append(3) + dcll.insert_after(dcll[-1], 4) + dcll.insert_after(dcll[2], 6) + dcll.insert_before(dcll[4], 1) + dcll.insert_before(dcll[0], 7) + dcll.insert_at(0, 2) + dcll.insert_at(-1, 9) + dcll.extract(2) + dcll.extract(0) + dcll.extract(-1) + dcll[-2].data = 0 + assert str(dcll) == "[7, 5, 1, 6, 1, 0, 9]" + assert len(dcll) == 7 + assert raises(IndexError, lambda: dcll.insert_at(8, None)) + assert raises(IndexError, lambda: dcll.extract(20)) + dcll_copy = copy.deepcopy(dcll) + for i in range(len(dcll)): + if i%2 == 0: + dcll.pop_left() + else: + dcll.pop_right() + assert str(dcll) == "[]" + for _ in range(len(dcll_copy)): + index = random.randint(0, len(dcll_copy) - 1) + dcll_copy.extract(index) + assert str(dcll_copy) == "[]" + assert raises(ValueError, lambda: dcll_copy.extract(1))