Skip to content

New doubly linkedlist PR: pull/2573 #3380

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 1 commit into from
Oct 16, 2020
Merged
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
253 changes: 253 additions & 0 deletions data_structures/linked_list/doubly_linked_list_two.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
"""
- 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 Node:
def __init__(self, data: int, previous=None, next_node=None):
self.data = data
self.previous = previous
self.next = next_node

def __str__(self) -> str:
return f"{self.data}"

def get_data(self) -> int:
return self.data

def get_next(self):
return self.next

def get_previous(self):
return self.previous


class LinkedListIterator:
def __init__(self, head):
self.current = head

def __iter__(self):
return self

def __next__(self):
if not self.current:
raise StopIteration
else:
value = self.current.get_data()
self.current = self.current.get_next()
return value


class LinkedList:
def __init__(self):
self.head = None # First node in list
self.tail = None # Last node in list

def __str__(self):
current = self.head
nodes = []
while current is not None:
nodes.append(current.get_data())
current = current.get_next()
return " ".join(str(node) for node in nodes)

def __contains__(self, value: int):
current = self.head
while current:
if current.get_data() == value:
return True
current = current.get_next()
return False

def __iter__(self):
return LinkedListIterator(self.head)

def get_head_data(self):
if self.head:
return self.head.get_data()
return None

def get_tail_data(self):
if self.tail:
return self.tail.get_data()
return None

def set_head(self, node: Node) -> None:

if self.head is None:
self.head = node
self.tail = node
else:
self.insert_before_node(self.head, node)

def set_tail(self, node: Node) -> None:
if self.head is None:
self.set_head(node)
else:
self.insert_after_node(self.tail, node)

def insert(self, value: int) -> None:
node = Node(value)
if self.head is None:
self.set_head(node)
else:
self.set_tail(node)

def insert_before_node(self, node: Node, node_to_insert: Node) -> None:
node_to_insert.next = node
node_to_insert.previous = node.previous

if node.get_previous() is None:
self.head = node_to_insert
else:
node.previous.next = node_to_insert

node.previous = node_to_insert

def insert_after_node(self, node: Node, node_to_insert: Node) -> None:
node_to_insert.previous = node
node_to_insert.next = node.next

if node.get_next() is None:
self.tail = node_to_insert
else:
node.next.previous = node_to_insert

node.next = node_to_insert

def insert_at_position(self, position: int, value: int) -> None:
current_position = 1
new_node = Node(value)
node = self.head
while node:
if current_position == position:
self.insert_before_node(node, new_node)
return None
current_position += 1
node = node.next
self.insert_after_node(self.tail, new_node)

def get_node(self, item: int) -> Node:
node = self.head
while node:
if node.get_data() == item:
return node
node = node.get_next()
raise Exception("Node not found")

def delete_value(self, value):
node = self.get_node(value)

if node is not None:
if node == self.head:
self.head = self.head.get_next()

if node == self.tail:
self.tail = self.tail.get_previous()

self.remove_node_pointers(node)

@staticmethod
def remove_node_pointers(node: Node) -> None:
if node.get_next():
node.next.previous = node.previous

if node.get_previous():
node.previous.next = node.next

node.next = None
node.previous = None

def is_empty(self):
return self.head is None


def create_linked_list() -> None:
"""
>>> new_linked_list = LinkedList()
>>> new_linked_list.get_head_data() is None
True
>>> new_linked_list.get_tail_data() is None
True
>>> new_linked_list.is_empty()
True
>>> new_linked_list.insert(10)
>>> new_linked_list.get_head_data()
10
>>> new_linked_list.get_tail_data()
10
>>> new_linked_list.insert_at_position(position=3, value=20)
>>> new_linked_list.get_head_data()
10
>>> new_linked_list.get_tail_data()
20
>>> new_linked_list.set_head(Node(1000))
>>> new_linked_list.get_head_data()
1000
>>> new_linked_list.get_tail_data()
20
>>> new_linked_list.set_tail(Node(2000))
>>> new_linked_list.get_head_data()
1000
>>> new_linked_list.get_tail_data()
2000
>>> for value in new_linked_list:
... print(value)
1000
10
20
2000
>>> new_linked_list.is_empty()
False
>>> for value in new_linked_list:
... print(value)
1000
10
20
2000
>>> 10 in new_linked_list
True
>>> new_linked_list.delete_value(value=10)
>>> 10 in new_linked_list
False
>>> new_linked_list.delete_value(value=2000)
>>> new_linked_list.get_tail_data()
20
>>> new_linked_list.delete_value(value=1000)
>>> new_linked_list.get_tail_data()
20
>>> new_linked_list.get_head_data()
20
>>> for value in new_linked_list:
... print(value)
20
>>> new_linked_list.delete_value(value=20)
>>> for value in new_linked_list:
... print(value)
>>> for value in range(1,10):
... new_linked_list.insert(value=value)
>>> for value in new_linked_list:
... print(value)
1
2
3
4
5
6
7
8
9
"""


if __name__ == "__main__":
import doctest

doctest.testmod()