From 19cabb2f50395ddd15a2783820171bf157e81893 Mon Sep 17 00:00:00 2001 From: Prashant Rawat Date: Mon, 2 Mar 2020 18:58:05 +0530 Subject: [PATCH] DoublyLinkedList Fixed and SinglyLinkedList Added (#99) --- .../linear_data_structures/__init__.py | 1 + .../linear_data_structures/linked_lists.py | 233 +++++++++++++++++- .../tests/test_linked_lists.py | 48 +++- 3 files changed, 272 insertions(+), 10 deletions(-) diff --git a/pydatastructs/linear_data_structures/__init__.py b/pydatastructs/linear_data_structures/__init__.py index 0fe59d913..8a67a1537 100644 --- a/pydatastructs/linear_data_structures/__init__.py +++ b/pydatastructs/linear_data_structures/__init__.py @@ -12,6 +12,7 @@ __all__.extend(arrays.__all__) from .linked_lists import ( + SinglyLinkedList, DoublyLinkedList ) __all__.extend(linked_lists.__all__) diff --git a/pydatastructs/linear_data_structures/linked_lists.py b/pydatastructs/linear_data_structures/linked_lists.py index 38f5e5e7d..0fc0cd21d 100644 --- a/pydatastructs/linear_data_structures/linked_lists.py +++ b/pydatastructs/linear_data_structures/linked_lists.py @@ -1,6 +1,7 @@ from pydatastructs.utils.misc_util import _check_type, LinkedListNode __all__ = [ + 'SinglyLinkedList', 'DoublyLinkedList' ] @@ -111,6 +112,8 @@ def insert_after(self, prev_node, data): links=['next', 'prev'], addrs=[None, None]) new_node.next = prev_node.next + if new_node.next is not None: + new_node.next.prev = new_node prev_node.next = new_node new_node.prev = prev_node @@ -119,7 +122,7 @@ def insert_after(self, prev_node, data): def insert_before(self, next_node, data): """ - Inserts a new node before the new_node. + Inserts a new node before the next_node. Parameters ========== @@ -138,8 +141,9 @@ def insert_before(self, next_node, data): new_node.prev = next_node.prev next_node.prev = new_node new_node.next = next_node - - if new_node.prev is None: + if new_node.prev is not None: + new_node.prev.next = new_node + else: self.head = new_node def insert_at(self, index, data): @@ -281,3 +285,226 @@ def __getitem__(self, index): current_node = current_node.next counter += 1 return current_node + +class SinglyLinkedList(LinkedList): + """ + Represents Singly Linked List + + Examples + ======== + + >>> from pydatastructs import SinglyLinkedList + >>> sll = SinglyLinkedList() + >>> sll.append(6) + >>> sll[0].data + 6 + >>> sll.head.data + 6 + >>> sll.append(5) + >>> sll.append_left(2) + >>> print(sll) + [2, 6, 5] + >>> sll[0].data = 7.2 + >>> sll.extract(1).data + 6 + >>> print(sll) + [7.2, 5] + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Singly_linked_list + + """ + __slots__ = ['head', 'tail', 'size'] + + def __new__(cls): + obj = LinkedList.__new__(cls) + obj.head = None + obj.tail = None + obj.size = 0 + return obj + + def append_left(self, data): + """ + Pushes a new node at the start i.e., + the left of the list. + + Parameters + ========== + + data + Any valid data to be stored in the node. + """ + self.insert_at(0, data) + + def append(self, data): + """ + Appends a new node at the end of the list. + + Parameters + ========== + + data + Any valid data to be stored in the node. + """ + self.insert_at(self.size, data) + + 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. + """ + self.size += 1 + new_node = LinkedListNode(data, + links=['next'], + addrs=[None]) + new_node.next = prev_node.next + prev_node.next = new_node + + if new_node.next is None: + self.tail = new_node + + 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. + """ + if self.size == 0 and (index in (0, -1)): + index = 0 + + if index < 0: + index = self.size + index + + if index > self.size: + raise IndexError('%d index is out of range.'%(index)) + + self.size += 1 + new_node = LinkedListNode(data, + links=['next'], + addrs=[None]) + if self.size == 1: + self.head, self.tail = \ + new_node, new_node + else: + counter = 0 + current_node = self.head + prev_node = None + while counter != index: + prev_node = current_node + current_node = current_node.next + counter += 1 + new_node.next = current_node + if prev_node is not None: + prev_node.next = new_node + if new_node.next is None: + self.tail = new_node + if index == 0: + self.head = new_node + + def pop_left(self): + """ + Extracts the Node from the left + i.e. start of the list. + + Returns + ======= + + old_head: LinkedListNode + The leftmost element of linked + list. + """ + self.extract(0) + + def pop_right(self): + """ + Extracts the node from the right + of the linked list. + + Returns + ======= + + old_tail: LinkedListNode + The leftmost element of linked + list. + """ + self.extract(-1) + + 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. + """ + if self.is_empty: + raise ValueError("The list is empty.") + + if index < 0: + index = self.size + index + + if index >= self.size: + raise IndexError('%d is out of range.'%(index)) + + self.size -= 1 + counter = 0 + current_node = self.head + prev_node = None + while counter != index: + prev_node = current_node + current_node = current_node.next + counter += 1 + if prev_node is not None: + prev_node.next = current_node.next + if index == 0: + self.head = current_node.next + if index == self.size: + self.tail = prev_node + return current_node + + def __getitem__(self, index): + """ + Returns + ======= + + current_node: LinkedListNode + The node at given index. + """ + if index < 0: + index = self.size + index + + if index >= self.size: + raise IndexError('%d index is out of range.'%(index)) + + counter = 0 + current_node = self.head + while counter != index: + current_node = current_node.next + counter += 1 + return current_node diff --git a/pydatastructs/linear_data_structures/tests/test_linked_lists.py b/pydatastructs/linear_data_structures/tests/test_linked_lists.py index 4709fd65f..0087031f4 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 +from pydatastructs.linear_data_structures import DoublyLinkedList, SinglyLinkedList from pydatastructs.utils.raises_util import raises import copy, random @@ -10,18 +10,19 @@ def test_DoublyLinkedList(): dll.append(1) dll.append_left(2) dll.append(3) - dll.insert_after(dll[1], 4) - dll.insert_after(dll[-1], 6) - dll.insert_before(dll[0], 1) + dll.insert_after(dll[-1], 4) + dll.insert_after(dll[2], 6) + dll.insert_before(dll[4], 1) + dll.insert_before(dll[0], 7) dll.insert_at(0, 2) dll.insert_at(-1, 9) dll.extract(2) dll.extract(0) dll.extract(-1) dll[-2].data = 0 - assert str(dll) == "[1, 5, 4, 1, 0, 9]" - assert len(dll) == 6 - assert raises(IndexError, lambda: dll.insert_at(7, None)) + assert str(dll) == "[7, 5, 1, 6, 1, 0, 9]" + assert len(dll) == 7 + assert raises(IndexError, lambda: dll.insert_at(8, None)) assert raises(IndexError, lambda: dll.extract(20)) dll_copy = copy.deepcopy(dll) for i in range(len(dll)): @@ -35,3 +36,36 @@ def test_DoublyLinkedList(): dll_copy.extract(index) assert str(dll_copy) == "[]" assert raises(ValueError, lambda: dll_copy.extract(1)) + +def test_SinglyLinkedList(): + random.seed(1000) + sll = SinglyLinkedList() + assert raises(IndexError, lambda: sll[2]) + sll.append_left(5) + sll.append(1) + sll.append_left(2) + sll.append(3) + sll.insert_after(sll[1], 4) + sll.insert_after(sll[-1], 6) + sll.insert_at(0, 2) + sll.insert_at(-1, 9) + sll.extract(2) + sll.extract(0) + sll.extract(-1) + sll[-2].data = 0 + assert str(sll) == "[2, 4, 1, 0, 9]" + assert len(sll) == 5 + assert raises(IndexError, lambda: sll.insert_at(6, None)) + assert raises(IndexError, lambda: sll.extract(20)) + sll_copy = copy.deepcopy(sll) + for i in range(len(sll)): + if i%2 == 0: + sll.pop_left() + else: + sll.pop_right() + assert str(sll) == "[]" + for _ in range(len(sll_copy)): + index = random.randint(0, len(sll_copy) - 1) + sll_copy.extract(index) + assert str(sll_copy) == "[]" + assert raises(ValueError, lambda: sll_copy.extract(1))