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:
Du Yuanchao 2020-10-21 22:31:09 +08:00 committed by GitHub
parent 9b95e4f662
commit 5e642607c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

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