mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-30 16:31:08 +00:00
Update doubly linked list (#3619)
* update doubly linked list * reformat code add more test * add test to iter * updating DIRECTORY.md Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
This commit is contained in:
parent
9b95e4f662
commit
5e642607c8
|
@ -1,89 +1,156 @@
|
|||
"""
|
||||
- A linked list is similar to an array, it holds values. However, links in a linked
|
||||
list do not have indexes.
|
||||
- This is an example of a double ended, doubly linked list.
|
||||
- Each link references the next link and the previous one.
|
||||
- A Doubly Linked List (DLL) contains an extra pointer, typically called previous
|
||||
pointer, together with next pointer and data which are there in singly linked list.
|
||||
- Advantages over SLL - It can be traversed in both forward and backward direction.
|
||||
Delete operation is more efficient"""
|
||||
|
||||
|
||||
class LinkedList:
|
||||
"""
|
||||
>>> linked_list = LinkedList()
|
||||
>>> linked_list.insert_at_head("a")
|
||||
>>> linked_list.insert_at_tail("b")
|
||||
>>> linked_list.delete_tail()
|
||||
'b'
|
||||
>>> linked_list.is_empty
|
||||
False
|
||||
>>> linked_list.delete_head()
|
||||
'a'
|
||||
>>> linked_list.is_empty
|
||||
True
|
||||
https://en.wikipedia.org/wiki/Doubly_linked_list
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.head = None # First node in list
|
||||
self.tail = None # Last node in list
|
||||
|
||||
class Node:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.previous = None
|
||||
self.next = None
|
||||
|
||||
def __str__(self):
|
||||
current = self.head
|
||||
nodes = []
|
||||
while current is not None:
|
||||
nodes.append(current)
|
||||
current = current.next
|
||||
return " ".join(str(node) for node in nodes)
|
||||
return f"{self.data}"
|
||||
|
||||
|
||||
class DoublyLinkedList:
|
||||
def __init__(self):
|
||||
self.head = None
|
||||
self.tail = None
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
>>> linked_list = DoublyLinkedList()
|
||||
>>> linked_list.insert_at_head('b')
|
||||
>>> linked_list.insert_at_head('a')
|
||||
>>> linked_list.insert_at_tail('c')
|
||||
>>> tuple(linked_list)
|
||||
('a', 'b', 'c')
|
||||
"""
|
||||
node = self.head
|
||||
while node:
|
||||
yield node.data
|
||||
node = node.next
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
>>> linked_list = DoublyLinkedList()
|
||||
>>> linked_list.insert_at_tail('a')
|
||||
>>> linked_list.insert_at_tail('b')
|
||||
>>> linked_list.insert_at_tail('c')
|
||||
>>> str(linked_list)
|
||||
'a->b->c'
|
||||
"""
|
||||
return "->".join([str(item) for item in self])
|
||||
|
||||
def __len__(self):
|
||||
"""
|
||||
>>> linked_list = DoublyLinkedList()
|
||||
>>> for i in range(0, 5):
|
||||
... linked_list.insert_at_nth(i, i + 1)
|
||||
>>> len(linked_list) == 5
|
||||
True
|
||||
"""
|
||||
return len(tuple(iter(self)))
|
||||
|
||||
def insert_at_head(self, data):
|
||||
self.insert_at_nth(0, data)
|
||||
|
||||
def insert_at_tail(self, data):
|
||||
self.insert_at_nth(len(self), data)
|
||||
|
||||
def insert_at_nth(self, index: int, data):
|
||||
"""
|
||||
>>> linked_list = DoublyLinkedList()
|
||||
>>> linked_list.insert_at_nth(-1, 666)
|
||||
Traceback (most recent call last):
|
||||
....
|
||||
IndexError: list index out of range
|
||||
>>> linked_list.insert_at_nth(1, 666)
|
||||
Traceback (most recent call last):
|
||||
....
|
||||
IndexError: list index out of range
|
||||
>>> linked_list.insert_at_nth(0, 2)
|
||||
>>> linked_list.insert_at_nth(0, 1)
|
||||
>>> linked_list.insert_at_nth(2, 4)
|
||||
>>> linked_list.insert_at_nth(2, 3)
|
||||
>>> str(linked_list)
|
||||
'1->2->3->4'
|
||||
>>> linked_list.insert_at_nth(5, 5)
|
||||
Traceback (most recent call last):
|
||||
....
|
||||
IndexError: list index out of range
|
||||
"""
|
||||
if not 0 <= index <= len(self):
|
||||
raise IndexError("list index out of range")
|
||||
new_node = Node(data)
|
||||
if self.is_empty:
|
||||
self.tail = new_node
|
||||
self.head = new_node
|
||||
else:
|
||||
if self.head is None:
|
||||
self.head = self.tail = new_node
|
||||
elif index == 0:
|
||||
self.head.previous = new_node
|
||||
new_node.next = self.head
|
||||
self.head = new_node
|
||||
|
||||
def delete_head(self) -> str:
|
||||
if self.is_empty:
|
||||
return "List is empty"
|
||||
|
||||
head_data = self.head.data
|
||||
if self.head.next:
|
||||
self.head = self.head.next
|
||||
self.head.previous = None
|
||||
|
||||
else: # If there is no next previous node
|
||||
self.head = None
|
||||
self.tail = None
|
||||
|
||||
return head_data
|
||||
|
||||
def insert_at_tail(self, data):
|
||||
new_node = Node(data)
|
||||
if self.is_empty:
|
||||
self.tail = new_node
|
||||
self.head = new_node
|
||||
else:
|
||||
elif index == len(self):
|
||||
self.tail.next = new_node
|
||||
new_node.previous = self.tail
|
||||
self.tail = new_node
|
||||
else:
|
||||
temp = self.head
|
||||
for i in range(0, index):
|
||||
temp = temp.next
|
||||
temp.previous.next = new_node
|
||||
new_node.previous = temp.previous
|
||||
new_node.next = temp
|
||||
temp.previous = new_node
|
||||
|
||||
def delete_tail(self) -> str:
|
||||
if self.is_empty:
|
||||
return "List is empty"
|
||||
def delete_head(self):
|
||||
return self.delete_at_nth(0)
|
||||
|
||||
tail_data = self.tail.data
|
||||
if self.tail.previous:
|
||||
def delete_tail(self):
|
||||
return self.delete_at_nth(len(self) - 1)
|
||||
|
||||
def delete_at_nth(self, index: int):
|
||||
"""
|
||||
>>> linked_list = DoublyLinkedList()
|
||||
>>> linked_list.delete_at_nth(0)
|
||||
Traceback (most recent call last):
|
||||
....
|
||||
IndexError: list index out of range
|
||||
>>> for i in range(0, 5):
|
||||
... linked_list.insert_at_nth(i, i + 1)
|
||||
>>> linked_list.delete_at_nth(0) == 1
|
||||
True
|
||||
>>> linked_list.delete_at_nth(3) == 5
|
||||
True
|
||||
>>> linked_list.delete_at_nth(1) == 3
|
||||
True
|
||||
>>> str(linked_list)
|
||||
'2->4'
|
||||
>>> linked_list.delete_at_nth(2)
|
||||
Traceback (most recent call last):
|
||||
....
|
||||
IndexError: list index out of range
|
||||
"""
|
||||
if not 0 <= index <= len(self) - 1:
|
||||
raise IndexError("list index out of range")
|
||||
delete_node = self.head # default first node
|
||||
if len(self) == 1:
|
||||
self.head = self.tail = None
|
||||
elif index == 0:
|
||||
self.head = self.head.next
|
||||
self.head.previous = None
|
||||
elif index == len(self) - 1:
|
||||
delete_node = self.tail
|
||||
self.tail = self.tail.previous
|
||||
self.tail.next = None
|
||||
else: # if there is no previous node
|
||||
self.head = None
|
||||
self.tail = None
|
||||
|
||||
return tail_data
|
||||
else:
|
||||
temp = self.head
|
||||
for i in range(0, index):
|
||||
temp = temp.next
|
||||
delete_node = temp
|
||||
temp.next.previous = temp.previous
|
||||
temp.previous.next = temp.next
|
||||
return delete_node.data
|
||||
|
||||
def delete(self, data) -> str:
|
||||
current = self.head
|
||||
|
@ -105,16 +172,55 @@ class LinkedList:
|
|||
current.next.previous = current.previous # 1 <--> 3
|
||||
return data
|
||||
|
||||
@property
|
||||
def is_empty(self): # return True if the list is empty
|
||||
return self.head is None
|
||||
def is_empty(self):
|
||||
"""
|
||||
>>> linked_list = DoublyLinkedList()
|
||||
>>> linked_list.is_empty()
|
||||
True
|
||||
>>> linked_list.insert_at_tail(1)
|
||||
>>> linked_list.is_empty()
|
||||
False
|
||||
"""
|
||||
return len(self) == 0
|
||||
|
||||
|
||||
class Node:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.previous = None
|
||||
self.next = None
|
||||
def test_doubly_linked_list() -> None:
|
||||
"""
|
||||
>>> test_doubly_linked_list()
|
||||
"""
|
||||
linked_list = DoublyLinkedList()
|
||||
assert linked_list.is_empty() is True
|
||||
assert str(linked_list) == ""
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.data}"
|
||||
try:
|
||||
linked_list.delete_head()
|
||||
assert False # This should not happen.
|
||||
except IndexError:
|
||||
assert True # This should happen.
|
||||
|
||||
try:
|
||||
linked_list.delete_tail()
|
||||
assert False # This should not happen.
|
||||
except IndexError:
|
||||
assert True # This should happen.
|
||||
|
||||
for i in range(10):
|
||||
assert len(linked_list) == i
|
||||
linked_list.insert_at_nth(i, i + 1)
|
||||
assert str(linked_list) == "->".join(str(i) for i in range(1, 11))
|
||||
|
||||
linked_list.insert_at_head(0)
|
||||
linked_list.insert_at_tail(11)
|
||||
assert str(linked_list) == "->".join(str(i) for i in range(0, 12))
|
||||
|
||||
assert linked_list.delete_head() == 0
|
||||
assert linked_list.delete_at_nth(9) == 10
|
||||
assert linked_list.delete_tail() == 11
|
||||
assert len(linked_list) == 9
|
||||
assert str(linked_list) == "->".join(str(i) for i in range(1, 10))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from doctest import testmod
|
||||
|
||||
testmod()
|
||||
|
|
Loading…
Reference in New Issue
Block a user