Skip to content

Commit

Permalink
DoublyLinkedList Fixed and SinglyLinkedList Added (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
prshnt19 authored Mar 2, 2020
1 parent 2d702b7 commit 19cabb2
Show file tree
Hide file tree
Showing 3 changed files with 272 additions and 10 deletions.
1 change: 1 addition & 0 deletions pydatastructs/linear_data_structures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
__all__.extend(arrays.__all__)

from .linked_lists import (
SinglyLinkedList,
DoublyLinkedList
)
__all__.extend(linked_lists.__all__)
233 changes: 230 additions & 3 deletions pydatastructs/linear_data_structures/linked_lists.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pydatastructs.utils.misc_util import _check_type, LinkedListNode

__all__ = [
'SinglyLinkedList',
'DoublyLinkedList'
]

Expand Down Expand Up @@ -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

Expand All @@ -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
==========
Expand All @@ -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):
Expand Down Expand Up @@ -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
48 changes: 41 additions & 7 deletions pydatastructs/linear_data_structures/tests/test_linked_lists.py
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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)):
Expand All @@ -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))

0 comments on commit 19cabb2

Please sign in to comment.