-
Notifications
You must be signed in to change notification settings - Fork 272
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 Linked Lists in linear-data-structures #51
Conversation
Codecov Report
@@ Coverage Diff @@
## master #51 +/- ##
============================================
- Coverage 98.003% 88.764% -9.24%
============================================
Files 17 18 +1
Lines 1102 1246 +144
============================================
+ Hits 1080 1106 +26
- Misses 22 140 +118
|
self.prev = NoneType | ||
#Alternative constructor for Single Linked List | ||
@classmethod | ||
def singleLink(obj, data): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
singleLink
-> single_link
Why it's a class method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second method is supposed to be an alternative constructor for the Node class when applied to Single LInked Lists. In that case single LInk will be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's analyse the situation.
If we use two separate classes for Node
one for Doubly Linked lists and other for singly linked lists, then it will lead to unnecessary code.
However, if we use Node
for Doubly Linked Lists to support the singly linked lists then it will lead to wastage of space.
There can be multiple links in a single node like more than 2, in that case both of the above solutions will become incompatible.
So, what about passing an extra parameter in the constructor specifying the number of links it will have. In fact, we can take input from the user, the name of each link and create the attributes dynamically, something like in this answer https://stackoverflow.com/a/1325768
I will think more about it's feasibility.
The code coverage is very low. Please add tests for this class. Shift the |
Sure, although can you explain how code coverage works? I heard there needs to bbe unit test coverage what does it mean? |
You can use |
Thanks for the changes. I will test in my system. Will make changes to your code and will share the diff which you can apply and push the changes later. |
diff --git a/pydatastructs/linear_data_structures/__init__.py b/pydatastructs/linear_data_structures/__init__.py
index a21fe38..0fe59d9 100644
--- a/pydatastructs/linear_data_structures/__init__.py
+++ b/pydatastructs/linear_data_structures/__init__.py
@@ -9,8 +9,9 @@ from .arrays import (
OneDimensionalArray,
DynamicOneDimensionalArray
)
+__all__.extend(arrays.__all__)
from .linked_lists import (
DoublyLinkedList
)
-__all__.extend(arrays.__all__)
+__all__.extend(linked_lists.__all__)
diff --git a/pydatastructs/linear_data_structures/linked_lists.py b/pydatastructs/linear_data_structures/linked_lists.py
index 974ab67..5915338 100644
--- a/pydatastructs/linear_data_structures/linked_lists.py
+++ b/pydatastructs/linear_data_structures/linked_lists.py
@@ -1,330 +1,284 @@
from __future__ import print_function, division
-from pydatastructs.utils.misc_util import _check_type, NoneType
-from pydatastructs.utils import (
- Node
-)
-
-__author__ = 'rohansingh9001'
+from pydatastructs.utils.misc_util import _check_type, LinkedListNode
__all__ = [
- 'DoublyLinkedList',
- 'LinkedList'
+ 'DoublyLinkedList'
]
class LinkedList(object):
'''
- Abstract class for Linked List in pydatastructs.
+ Abstract class for Linked List.
'''
pass
-# Class to create a Doubly Linked List
-class DoublyLinkedList(LinkedList):
-
-
- '''
- A Doubly Linked List Class (abb. DLL)
-
- Parameters
- ==========
-
- None
+class DoublyLinkedList(LinkedList):
+ """
+ Represents Doubly Linked List
Examples
========
- >>> from pydatastructs import DoublyLinkedLIst as DLL
- >>> dll = DLL()
+
+ >>> from pydatastructs import DoublyLinkedList
+ >>> dll = DoublyLinkedList()
>>> dll.append(6)
- >>> arr[0]
+ >>> dll[0].data
6
- >>> dll.head
+ >>> dll.head.data
6
>>> dll.append(5)
- >>> dll.appendleft(2)
+ >>> dll.append_left(2)
>>> print(dll)
- [2,6,5]
- >>> dll[0] = 7.2
- >>> dll[0]
- 7.2
- >>> dll.pop(1)
+ [2, 6, 5]
+ >>> dll[0].data = 7.2
+ >>> dll.extract(1).data
6
>>> print(dll)
- [2,5]
+ [7.2, 5]
References
==========
-
- https://www.geeksforgeeks.org/doubly-linked-list/
- '''
- __slots__ = ['head','tail','length']
-
- def __init__(self):
- self.head = NoneType
- self.tail = NoneType
- self.length = 0
-
- def appendleft(self,data):
- '''
- appendleft:
- takes parameters - data
- data: type
- A valid object type
- Should be convertible to string using str() method to
- use print() method on instance.
- action - Pushes a new node at the start i.e. left of the DLL.
- '''
- self.length += 1
- newNode = Node(data)
- if self.head is not NoneType:
- self.head.prev = newNode
- newNode.next = self.head
- self.head = newNode
-
- if newNode.next == NoneType:
- self.tail = newNode
- if newNode.prev == NoneType:
- self.head = newNode
-
+ .. [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):
- '''
- append:
- takes parameters - data
- data: type
- A valid object type.
- Should be convertible to string using str() method to
- use print() method on instance.
- action - Appends a new node at the end i.e. the right of the DLL.
- '''
- self.length += 1
- newNode = Node(data)
- if self.tail is not NoneType:
- self.tail.next = newNode
- newNode.prev = self.tail
- self.tail = newNode
-
- if newNode.next == NoneType:
- self.tail = newNode
- if newNode.prev == NoneType:
- self.head = newNode
-
- def insertAfter(self, prevNode, data):
- '''
- insertAfter:
- takes parameters - prevNode, data
- prevNode: Node type
- An object of Node class
- data: type
- A valid object type.
- Should be convertible to string using str() method to
- use print() method in instance.
- action - Inserts a new node after the prevNode.
- '''
- self.length += 1
- newNode = Node(data)
- newNode.next = prevNode.next
- prevNode.next = newNode
- newNode.prev = prevNode
-
- if newNode.next == NoneType:
- self.tail = newNode
- if newNode.prev == NoneType:
- self.head = newNode
-
- def insertBefore(self, nextNode, data):
- '''
- insertBefore:
- takes parameters - nextNode, data
- prevNode: Node type
- An object of Node class
- data: type
- A valid object type.
- Should be convertible to string using str() method to
- use print() method in instance.
- action - Inserts a new node before the newNode.
- '''
- self.length += 1
- newNode = Node(data)
- newNode.prev = nextNode.prev
- nextNode.prev = newNode
- newNode.next = nextNode
-
- if newNode.next == NoneType:
- self.tail = newNode
- if newNode.prev == NoneType:
- self.head = newNode
-
- def insertAt(self, index, data):
- '''
- insertAt:
- takes parameters - index, data
- index: int type
- An integer i such that 0<= i <= length, where length
- refers to the length of the List.
- data: type
- A valid object type.
- Should be convertible to string using str() method to
- use print() method in instance.
- action - Inserts a new node at the input index.
- '''
- if index > self.length or index < 0 or not (_check_type(index, int)):
- raise ValueError('Index input out of range/Index is expected to be an Integer.')
+ """
+ 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:
- if index == 0:
- self.appendleft(data)
- elif index == self.length:
- self.appendright(data)
- else:
- self.length += 1
- newNode = Node(data)
- counter = 0
- currentNode = self.head
- while counter != index:
- currentNode = currentNode.next
- counter += 1
- currentNode.prev.next = newNode
- newNode.prev = currentNode.prev
- currentNode.prev = newNode
- newNode.next = currentNode
-
- if newNode.next == NoneType:
- self.tail = newNode
- if newNode.prev == NoneType:
- self.head = newNode
-
- def popleft(self):
- '''
- popleft:
- takes parameters - None
- action - Removes the Node from the left i.e. start of the DLL
- and returns the data from the Node.
- '''
- self.length -= 1
- oldHead = self.head
- oldHead.next.prev = NoneType
- self.head = oldHead.next
- return oldHead.data
-
- def popright(self):
- '''
- popright:
- takes parameters - None
- action - Removes the Node from the right i.e. end of the DLL
- and returns the data from the Node.
- '''
- self.length -= 1
- oldTail = self.tail
- oldTail.prev.next = NoneType
- self.tail = oldTail.prev
- return oldTail.data
-
- def pop(self, index=0):
- '''
- pop:
- takes parameters - index
- index: int type
- An integer i such that 0<= i <= length, where length
- refers to the length of the List.
- action - Removes the Node at the index of the DLL and returns
- the data from the Node.
- '''
- if index > self.length or index < 0 or not (_check_type(index, int)):
- raise ValueError('Index input out of range/Index is expected to be an Integer.')
- else:
- if index == 0:
- self.popleft()
- elif index == self.length:
- self.popright()
- else:
- self.length -= 1
- counter = 0
- currentNode = self.head
- while counter != index:
- currentNode = currentNode.next
- counter += 1
- currentNode.prev.next = currentNode.next
- currentNode.next.prev = currentNode.prev
- return currentNode.data
-
- def __getitem__(self, index):
- '''
- __getitem__:
- takes parameters - index
- index: int type
- An integer i such that 0<= i <= length, where length
- refers to the length of the List.
- action - Returns the data of the Node at index.
- '''
- if index > self.length or index < 0 or not (_check_type(index, int)):
- raise ValueError('Index input out of range/Index is expected to be an Integer.')
- else:
- counter = 0
- currentNode = self.head
- while counter != index:
- currentNode = currentNode.next
- counter += 1
- return currentNode.data
-
- def __setitem__(self, index, data):
- '''
- __setitem__:
- takes parameters - index, data
- index: int type
- An integer i such that 0<= i <= length, where length
- refers to the length of the List.
- data: type
- A valid object type.
- Should be convertible to string using str() method to use
- print() method in instance.
- action - Sets the data of the Node at the index to the input data.
- '''
- if index > self.length or index < 0 or not (_check_type(index, int)):
- raise ValueError('Index input out of range/Index is expected to be an Integer.')
- else:
counter = 0
- currentNode = self.head
+ current_node = self.head
+ prev_node = None
while counter != index:
- currentNode = currentNode.next
+ prev_node = current_node
+ current_node = current_node.next
counter += 1
- currentNode.data = data
+ 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
def __str__(self):
- '''
- __str__:
- takes parameters - None
- action - Prints the DLL in a list from from the start to the end.
- '''
+ """
+ For printing the linked list.
+ """
elements = []
- currentNode = self.head
- while currentNode is not NoneType:
- elements.append(currentNode.data)
- currentNode = currentNode.next
+ current_node = self.head
+ while current_node is not None:
+ elements.append(current_node.data)
+ current_node = current_node.next
return str(elements)
def __len__(self):
- '''
- __len__:
- takes parameters - None
- action - Returns the length of the DLL.
- '''
- return self.length
-
- def isEmpty(self):
- '''
- isEmpty:
- takes parameters - None
- action - Return a bool value to check if the DLL is empty or not.
- '''
- return self.length == 0
-
-if __name__ == '__main__':
- dll = DoublyLinkedList()
- dll.append(6)
- arr[0]
- dll.head
- dll.append(5)
- dll.appendleft(2)
- print(dll)
- dll[0] = 7.2
- dll[0]
- dll.pop(1)
- print(dll)
\ No newline at end of file
+ return self.size
+
+ @property
+ def is_empty(self):
+ return self.size == 0
diff --git a/pydatastructs/linear_data_structures/tests/test_linked_lists.py b/pydatastructs/linear_data_structures/tests/test_linked_lists.py
index e69de29..4709fd6 100644
--- a/pydatastructs/linear_data_structures/tests/test_linked_lists.py
+++ b/pydatastructs/linear_data_structures/tests/test_linked_lists.py
@@ -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))
diff --git a/pydatastructs/utils/__init__.py b/pydatastructs/utils/__init__.py
index 13c2a05..0923b7d 100644
--- a/pydatastructs/utils/__init__.py
+++ b/pydatastructs/utils/__init__.py
@@ -3,6 +3,6 @@ __all__ = []
from . import misc_util
from .misc_util import (
TreeNode,
- Node
+ LinkedListNode
)
__all__.extend(misc_util.__all__)
diff --git a/pydatastructs/utils/misc_util.py b/pydatastructs/utils/misc_util.py
index 707663e..b55dde7 100644
--- a/pydatastructs/utils/misc_util.py
+++ b/pydatastructs/utils/misc_util.py
@@ -1,8 +1,8 @@
from __future__ import print_function, division
__all__ = [
- 'TreeNode'
- 'Node'
+ 'TreeNode',
+ 'LinkedListNode'
]
_check_type = lambda a, t: isinstance(a, t)
@@ -42,47 +42,26 @@ class TreeNode(object):
"""
return str((self.left, self.key, self.data, self.right))
-
-# A linked list node
-class Node:
-
- '''
- Node class for Linked List and Doubly Linked List [ Intended for internal use and not to be imported]
+class LinkedListNode(object):
+ """
+ Represents node in linked lists.
Parameters
==========
-
- For Doubly Linked List use Default constructor(__init__):
- data: type
- A valid object type.
- Should be convertible to string using str() method to use print() method on instance
-
- For Single Linked List use Alternative constructor(singleLink):
- data: type
- A valid object type
- Should be convertible to string using str() method to use print() method on instance
-
- Note
- ====
+ data
+ Any valid data to be stored in the node.
+ """
- classmethod singleLink has been used for Node class for Single linked list due to non existence of a
- previous link between the nodes.
- '''
+ # __slots__ = ['data']
- __slots__ = ['data', 'next', 'prev']
-
- # Constructor to create a new node
- def __new__(self, data):
- self.data = data
- self.next = NoneType
- self.prev = NoneType
- #Alternative constructor for Single Linked List
- @classmethod
- def singleLink(obj, data):
+ def __new__(cls, data=None, links=['next'], addrs=[None]):
+ obj = object.__new__(cls)
obj.data = data
- obj.next = NoneType
+ for link, addr in zip(links, addrs):
+ obj.__setattr__(link, addr)
+ obj.__slots__ = ['data'] + links
return obj
-
+
def __str__(self):
return str(self.data)
diff --git a/pydatastructs/utils/raises_util.py b/pydatastructs/utils/raises_util.py
index 3d55b55..3a324d3 100644
--- a/pydatastructs/utils/raises_util.py
+++ b/pydatastructs/utils/raises_util.py
@@ -14,3 +14,4 @@ def raises(exception, code):
"""
with pytest.raises(exception):
code()
+ return True Apply the above diff on your branch i.e., Some feedbacks,
Thanks for the contribution. The test should pass once you push the changes here. |
Added linked-lists.py in linear-data-structures
Contains Doubly Linked List
References to other Issues or PRs or Relevant literature
Partially fixes issue #49 which requests to add linked lists.
Only Doubly Linked List added so far Single Linked List remains.
Brief description of what is fixed or changed
API for the additions are as follows -
API for Double Linked List:
1)A Doubly linked list should be initialised with
DoubleLinkedList()
2)Elements can be pushed at the start of the list using
.appendleft(data)
3)Elements can be appended at the end using
.appendright(data)
.pop(index)
5)
__getitem__
and__setitem__
support using python syntax,eg -
list[1]
list[5] =4
6)Print the Linked List in List form using
print()
functioneg -
if elements in linked list named
sample_list
are 1-2-3-4>>>print(sample_list)
[1,2,3,4]
7)Return length of the Linked List using
len()
function.8)
.popright()
and.popleft()
pops items from the right and the left respectively.9)
.insertAt(index, data)
inserts a new Node at the index.10)
.insertBefore(Node, data)
and.insertAfter(Node, data)
inserts a new Node before and after the input Node respectively.