Linked List implementation in Python
Intro
A linked list is a data structure which represents a sequence of nodes. In a singly linked list, each node points to the next node. In a doubly linked list, each node points to both the next and previous node.
To find nth element in the linked list, you have to iterate through n nodes which results in linear time complexity O(n). This is less efficient than array which has constant time complexity O(1) to access an element.
However, the advantage to use linked list is for insert and delete operation since it can be achieved in the constant time without shifting nodes around.
The following are the key methods to be implemented:
- insert(): add an item to the linked list
- remove(): remove a specified item from the linked list
- insertAt(index): insert an item at a given index
- deleteAt(index): delete an item at a given index
- find(): search an item in the linked list
- is_empty(): return if the linked list is empty or not
- count(): return the number of items in the linked list
Construct a node class
Before we implement a singly linked list, we need to construct a node class. The linked list would contain one or more nodes. Each node has a value and a pointer linked to the next node.
class Node:
def __init__(self):
self.val = None
self.next = None
def set_val(self, val):
self.val = val
def get_val(self):
return self.val
def set_next(self, next):
self.next = next
def get_next(self):
return self.next
Build a singly Linked List
The first function in the linked list class is the constructor, which will be called whenever a linked list is initialized.
It can include three attributes:
- head: the first node in the linked list
- tail: the last node in the linked list
- count: the number of nodes in the linked list
Then we can build the other required functions according to the node pointer manipulations.
class LinkedList:
def __init__(self):
self.head = None
self.tail = None
self.count = 0
def count_nodes(self):
return self.count
# insert at the tail of list
def insert(self, val):
new_node = Node()
new_node.set_val(val)
if self.head == None:
self.head = new_node
self.tail = new_node
else:
self.tail.next = new_node
self.tail = new_node
self.count += 1
# insert at the given index
def insertAt(self, val, index):
new_node = Node()
new_node.set_val(val)
current_idx = 0
current_node = self.head
previous_node = current_node
while current_idx < index and current_node.next != None:
previous_node = current_node
current_node = current_node.next
current_idx += 1
if current_idx == index:
# found the index
new_node.next = current_node
previous_node.next = new_node
# increase counter
self.count += 1
else:
print("the specified index {} is invalid".format(index))
# delete the specified node
def deleteAt(self, index):
current_idx = 0
current_node = self.head
previous_node = current_node
while True:
if current_idx < index:
if current_node.next != None:
# move on to the next node
previous_node = current_node
current_node = current_node.next
current_idx += 1
else:
print("invalid index {}".format(index))
break
else:
# found the index, delete it
if current_node.next == None:
# this is tail
previous_node.next = None
self.tail = previous_node
else:
# this is not tail
previous_node.next = current_node.next
# decrease the counter
self.count -= 1
break
def find(self, index):
current_node = self.head
current_idx = 0
while current_node.next != None:
if current_idx == index:
return current_node.get_val()
else:
current_node = current_node.next
print("invalid index {}".format(index))
def print_list(self):
current_node = self.head
current_idx = 0
while current_node.next != None:
print(current_node.get_val())
current_node = current_node.next
# print the last node
print(current_node.get_val())
print("count: {}".format(self.count_nodes()))
print()
Test the Linked List class
ll = LinkedList()
ll.insert(1)
ll.insert(2)
ll.insert(5)
ll.print_list()
ll.insertAt(4, 2)
ll.print_list()
ll.insertAt(3, 3)
ll.print_list()
ll.deleteAt(3)
ll.print_list()
Output:
1
2
5
count: 3
1
2
4
5
count: 4
1
2
4
3
5
count: 5
1
2
4
5
count: 4