Linked lists are characterized for no continuos memory. Unlike dynamic arrays, where each element is right next to each other, a collection of data in a linked list can be stored randomly in memory. Therefore, there is no garantee that one element is next to another. But, how could we keep our elements together? In order to keep our collection together, every element (called a node) will have a value and a reference that points to the next node
If we want traverse through our linked list, we will need to know where to begin and where to end. The first node in the linked list is refered as the head or the begining of the collection, and the last node is refered as the tail. Most linked lists use a bi-directional linking between nodes. This meas that each node will mantain a pointer to both the next and the previous element.
The process of inserting into a linked list will depend on where into the collection you want to add the new element.
Inserting a new element at the head is an easy 4 step process.
new_nodenew_node to the head: new_node.next = self.headnew_node to be the previous of the head: self.head.prev = new_nodenew_node to be the new head: self.head = new_nodeSimiliraly to inserting at the head, inserting at the tail is a 4 step process:
new_nodenew_node to the tail: new_node.prev = self.tailnew_node to be the next of the tail: self.tail.next = new_nodenew_node to be the new tail: self.tail = new_nodeThe process of inserting a new element into the middle is a bit different:
new_nodenew_node to the current node: new_node.prev = currentnew_node to the next node after the current: new_node.next = current.nextnew_node to be the previous of the node after the current: current.next.prev = new_nodenew_node to be the next of the current node: current.next = new_nodeself.head.next.prev = Noneself.head = self.head.next
self.tail.prev.next = Noneself.tail = self.tail.prev
current.next.prev = current.prevcurrent.prev.next = current.nextIf we want to create a linked list in python, we neeed to create 2 classes. The LinkedList class and the Node Class.
class LinkedList:
# Class to store the data of every element in the linked list
class Node:
def __init__(self, data):
self.next = None
self.prev = None
self.data = data
# Create an empty linked list
def __init__(self):
self.head = None
self.tail = None
Inside the LinkedList class we’ll add all the methods to manipulate our LinkedList. insert_head(), insert_tail(), remove_head(), remove_tail(), etc. You would follow the steps above to implement these methods.
Let’s practice creating the insert_head(), and insert_tail() methods for our linked list
Create the method insert_head(). Implement the method to handle the case if the list is empty or it’s not.
insert_head() method:
def insert_head(self, value):
new_node = LinkedList.Node(value)
if self.head is None:
self.head = new_node
self.tail = new_node
else:
new_node.next = self.head
self.head.prev = new_node
self.head = new_node
Create the method insert_tail(). Implement the method to handle the case if the list is empty or it’s not.
insert_tail() method:
def insert_tail(self, value):
new_node = LinkedList.Node(value)
if self.tail is None:
self.head = new_node
self.tail = new_node
else:
new_node.prev = self.tail
self.tail.next = new_node
self.tail = new_node
Python has a way to implement a Linked list using the deque method from collections.
# Import the deque method
from collections import deque
# Create the linked list with 3 elements
linked_list = deque([6, 2, 3, 1, 20])
# Insert something at the head
linked_list.appendleft(0)
# Insert something at the end
linked_list.append(9)
# Insert something at the middle .insert(i, value)
linked_list.insert(1, 8)
# Removing the head
linked_list.popleft()
# Removing the tail
linked_list.pop()
# Remove an element [i]
del linkedlist[2]
Try solving the following problem using linked lists.
In the practice problems, we implemented the insert_head() and insert_tail(). Now is your turn to implement the following methods in the Node class:
def remove_head()
def remove_tail()
def remove_node()
def insert_next()
You will need to add these two methods in the LinkedList class for your program to work ```python
def iter(self): curr = self.head # Start at the begining since this is a forward iteration. while curr is not None: yield curr.data # Provide (yield) each item to the user curr = curr.next # Go forward in the linked list
def str(self): output = “linkedlist[” first = True for value in self: if first: first = False else: output += “, “ output += str(value) output += “]” return output
Use the following as your test cases.
```python
linked_list = LinkedList()
linked_list.insert_head(3)
linked_list.insert_head(2)
linked_list.insert_head(7)
linked_list.insert_head(9)
linked_list.insert_head(10)
linked_list.insert_tail(20)
print(linked_list) #linkedlist[10, 9, 7, 2, 3, 20]
linked_list.remove_head()
print(linked_list) # linkedlist[9, 7, 2, 3, 20]
linked_list.remove_tail()
linked_list.remove_node(7)
print(linked_list) # linkedlist[9, 2, 3]
linked_list.insert_next(2, 11)
print(linked_list) # linkedlist[9, 2, 11, 3]
Compare your program with the solution provided Linked List Solution.
Stacks are very efficient. All of its primary functions have an efficiency of O(1) or constant time.
| Python syntax | Purpose | Performance | |
|---|---|---|---|
linked_list.appendleft(value) |
Adds a value at the head | O(1) | |
linked_list.append() |
Adds a value at the tail | O(1) | |
linked_list.insert(i, value) |
Insert a value at the index | O(n) | |
linked_list.popleft() |
Removes the first element | O(1) | |
linked_list.pop() |
Removes the last element | O(1) | |
linked_list.remove() |
Removes an element at index | O(n) | |
len(linked_list) |
Gets the size of the linked list | O(1) | |
if len(linked_list) == 0 |
Checks if a linked list is empty | O(1) |