Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DoublyLinkedList Fixed and SinglyLinkedList Added #99

Merged
merged 4 commits into from
Mar 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
prshnt19 marked this conversation as resolved.
Show resolved Hide resolved

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
>>> print(sll)
>>> str(sll)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore this for now. I have opened a different issue for this purpose.

[2, 6, 5]
>>> sll[0].data = 7.2
>>> sll.extract(1).data
6
>>> print(sll)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
>>> print(sll)
>>> str(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]"
czgdp1807 marked this conversation as resolved.
Show resolved Hide resolved
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]"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

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))