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

Added DoublyLinkedList #55

Merged
merged 3 commits into from
Dec 21, 2019
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
2 changes: 1 addition & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Gagandeep Singh<singh.23@iitj.ac.in>
Kartikei Mittal<kartikeimittal@gmail.com>
Umesh<23umesh.here@gmail.com>

Rohan Singh<singh.77@iitj.ac.in>
6 changes: 6 additions & 0 deletions pydatastructs/linear_data_structures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

from . import (
arrays,
linked_lists
)

from .arrays import (
OneDimensionalArray,
DynamicOneDimensionalArray
)
__all__.extend(arrays.__all__)

from .linked_lists import (
DoublyLinkedList
)
__all__.extend(linked_lists.__all__)
284 changes: 284 additions & 0 deletions pydatastructs/linear_data_structures/linked_lists.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
from __future__ import print_function, division
from pydatastructs.utils.misc_util import _check_type, LinkedListNode

__all__ = [
'DoublyLinkedList'
]

class LinkedList(object):
"""
Abstract class for Linked List.
"""
__slots__ = ['head', 'size']

def __len__(self):
return self.size

@property
def is_empty(self):
return self.size == 0

def __str__(self):
"""
For printing the linked list.
"""
elements = []
current_node = self.head
while current_node is not None:
elements.append(current_node.data)
current_node = current_node.next
return str(elements)

class DoublyLinkedList(LinkedList):
"""
Represents Doubly Linked List

Examples
========

>>> from pydatastructs import DoublyLinkedList
>>> dll = DoublyLinkedList()
>>> dll.append(6)
>>> dll[0].data
6
>>> dll.head.data
6
>>> dll.append(5)
>>> dll.append_left(2)
>>> print(dll)
[2, 6, 5]
>>> dll[0].data = 7.2
>>> dll.extract(1).data
6
>>> print(dll)
[7.2, 5]

References
==========

.. [1] https://en.wikipedia.org/wiki/Doubly_linked_list

"""
__slots__ = ['head', 'tail', 'size']

def __new__(cls):
obj = object.__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', 'prev'],
addrs=[None, None])
new_node.next = prev_node.next
prev_node.next = new_node
new_node.prev = prev_node

if new_node.next is None:
self.tail = new_node

def insert_before(self, next_node, data):
"""
Inserts a new node before the new_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.
"""
self.size += 1
new_node = LinkedListNode(data,
links=['next', 'prev'],
addrs=[None, None])
new_node.prev = next_node.prev
next_node.prev = new_node
new_node.next = next_node

if new_node.prev is None:
self.head = 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', 'prev'],
addrs=[None, 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.prev = prev_node
new_node.next = current_node
if prev_node is not None:
prev_node.next = new_node
if current_node is not None:
current_node.prev = new_node
if new_node.next is None:
self.tail = new_node
if new_node.prev is None:
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 current_node.next is not None:
current_node.next.prev = prev_node
if index == 0:
self.head = current_node.next
if index == self.size:
self.tail = current_node.prev
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
37 changes: 37 additions & 0 deletions pydatastructs/linear_data_structures/tests/test_linked_lists.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from pydatastructs.linear_data_structures import DoublyLinkedList
from pydatastructs.utils.raises_util import raises
import copy, random

def test_DoublyLinkedList():
random.seed(1000)
dll = DoublyLinkedList()
assert raises(IndexError, lambda: dll[2])
dll.append_left(5)
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_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 raises(IndexError, lambda: dll.extract(20))
dll_copy = copy.deepcopy(dll)
for i in range(len(dll)):
if i%2 == 0:
dll.pop_left()
else:
dll.pop_right()
assert str(dll) == "[]"
for _ in range(len(dll_copy)):
index = random.randint(0, len(dll_copy) - 1)
dll_copy.extract(index)
assert str(dll_copy) == "[]"
assert raises(ValueError, lambda: dll_copy.extract(1))
3 changes: 2 additions & 1 deletion pydatastructs/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from . import misc_util
from .misc_util import (
TreeNode
TreeNode,
LinkedListNode
)
__all__.extend(misc_util.__all__)
27 changes: 26 additions & 1 deletion pydatastructs/utils/misc_util.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from __future__ import print_function, division

__all__ = [
'TreeNode'
'TreeNode',
'LinkedListNode'
]

_check_type = lambda a, t: isinstance(a, t)
Expand Down Expand Up @@ -40,3 +41,27 @@ def __str__(self):
Used for printing.
"""
return str((self.left, self.key, self.data, self.right))

class LinkedListNode(object):
"""
Represents node in linked lists.

Parameters
==========

data
Any valid data to be stored in the node.
"""

# __slots__ = ['data']

def __new__(cls, data=None, links=['next'], addrs=[None]):
obj = object.__new__(cls)
obj.data = data
for link, addr in zip(links, addrs):
obj.__setattr__(link, addr)
obj.__slots__ = ['data'] + links
return obj

def __str__(self):
return str(self.data)
1 change: 1 addition & 0 deletions pydatastructs/utils/raises_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ def raises(exception, code):
"""
with pytest.raises(exception):
code()
return True