Skip to content

Update doubly linked list #3619

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

Merged
merged 4 commits into from
Oct 21, 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
5 changes: 5 additions & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,10 @@
* [Sdbm](https://github.com/TheAlgorithms/Python/blob/master/hashes/sdbm.py)
* [Sha1](https://github.com/TheAlgorithms/Python/blob/master/hashes/sha1.py)

## Knapsack
* [Knapsack](https://github.com/TheAlgorithms/Python/blob/master/knapsack/knapsack.py)
* [Test Knapsack](https://github.com/TheAlgorithms/Python/blob/master/knapsack/test_knapsack.py)

## Linear Algebra
* Src
* [Lib](https://github.com/TheAlgorithms/Python/blob/master/linear_algebra/src/lib.py)
Expand Down Expand Up @@ -502,6 +506,7 @@
* [Magicdiamondpattern](https://github.com/TheAlgorithms/Python/blob/master/other/magicdiamondpattern.py)
* [Markov Chain](https://github.com/TheAlgorithms/Python/blob/master/other/markov_chain.py)
* [Max Sum Sliding Window](https://github.com/TheAlgorithms/Python/blob/master/other/max_sum_sliding_window.py)
* [Median Of Two Arrays](https://github.com/TheAlgorithms/Python/blob/master/other/median_of_two_arrays.py)
* [Nested Brackets](https://github.com/TheAlgorithms/Python/blob/master/other/nested_brackets.py)
* [Palindrome](https://github.com/TheAlgorithms/Python/blob/master/other/palindrome.py)
* [Password Generator](https://github.com/TheAlgorithms/Python/blob/master/other/password_generator.py)
Expand Down
264 changes: 185 additions & 79 deletions data_structures/linked_list/doubly_linked_list.py
Original file line number Diff line number Diff line change
@@ -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"""

https://en.wikipedia.org/wiki/Doubly_linked_list
"""

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

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)

def insert_at_head(self, data):
new_node = Node(data)
if self.is_empty:
self.tail = new_node
self.head = new_node
else:
self.head.previous = new_node
new_node.next = self.head
self.head = new_node
return f"{self.data}"

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

else: # If there is no next previous node
self.head = None
self.tail = None
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)))

return head_data
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
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
else:
elif index == len(self):
self.tail.next = new_node
new_node.previous = self.tail
self.tail = new_node

def delete_tail(self) -> str:
if self.is_empty:
return "List is empty"

tail_data = self.tail.data
if self.tail.previous:
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_head(self):
return self.delete_at_nth(0)

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
Expand All @@ -105,16 +172,55 @@ def delete(self, data) -> str:
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 __str__(self):
return f"{self.data}"
def test_doubly_linked_list() -> None:
"""
>>> test_doubly_linked_list()
"""
linked_list = DoublyLinkedList()
assert linked_list.is_empty() is True
assert str(linked_list) == ""

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