In computer science, you can store a list of items in two ways, either at contiguous memory locations or at random memory locations.
If you store items at contiguous memory locations, we can access all the items if we know the memory location of the first item. For example, arrays or lists use contiguous memory locations to store a list of items.
A linked list is a linear data structure in which the items get stored at random memory locations. Each memory location of a linked list is called a node. These nodes are linked together using pointers.
Since the items are at random locations, we need to store the address of the next item in the current node. Hence, each node contains two fields; a data value to store the data of the current node and a pointer that stores the address of the next node.
The last node of the linked list contains a pointer that points to NULL. The head is a pointer that keeps a reference to the first element of a linked list.
Here is a pictorial representation of a linked list. This linked list has three nodes with values A, B, and C.

Linked list achieves optimized utilization of space since the nodes can reside anywhere in the memory. Also, you don’t need to define the size of a linked list in advance as it allocates the memory dynamically. These are some advantages of using linked lists over arrays.
If you haven’t understood the working of linked lists clearly, check out the following video.
Linked List Implementation using Python
Let’s implement linked lists using the Python programming language. We can create a class called Node, and whenever we want to add a node to the linked list, we can create an object of this class. This class will have two attributes: data and next.
class Node:
  def __init__(self,data):
    self.data = data
    self.next = None
Now, let’s create a class called LinkedList. We can create an object of this class to create a linked list. This class will have one attribute called head, which can be used as a pointer.
class LinkedList:
  def __init__(self):
    self.head = None
Inserting nodes
Let’s create a linked list and insert some nodes into it using Python. You can do insert nodes at the beginning of the linked list, end of the linked list, or after a specific node. We will see the Python code for inserting nodes in all three ways.
Inserting at the beginning of a linked list
class Node:
  def __init__(self,data):
    self.data = data
    self.next = None
class LinkedList:
  def __init__(self):
    self.head = None
  def insertAtBeginning(self,new_value):
    new_node = Node(new_value)
    new_node.next = self.head
    self.head = new_node
    print("New node inserted with data",new_value)
lList = LinkedList()
lList.insertAtBeginning(5)
lList.insertAtBeginning(7)

Inserting at the end of a linked list
class Node:
  def __init__(self,data):
    self.data = data
    self.next = None
class LinkedList:
  def __init__(self):
    self.head = None
  def insertAtEnd(self,new_value):
    new_node = Node(new_value)
    if self.head is None:
      self.head = new_node
      print("New node inserted with data",new_value)
      return  
    last_node = self.head
    while(last_node.next):
      last_node = last_node.next
    last_node.next = new_node
    print("New node inserted with data",new_value)
lList = LinkedList()
lList.insertAtEnd(2)
lList.insertAtEnd(4)

Inserting after a specific node
I have inserted a node using the insertAtBeginning method so that the previous node can’t be empty.
class Node:
  def __init__(self,data):
    self.data = data
    self.next = None
class LinkedList:
  def __init__(self):
    self.head = None
  def insertAtBeginning(self,new_value):
    new_node = Node(new_value)
    new_node.next = self.head
    self.head = new_node
    print("New node inserted with data",new_value)
  def insertAfter(self,prev_node,new_value):
    if prev_node == None:
      print("Previous node is empty")
    new_node = Node(new_value)
    new_node.next = prev_node.next
    prev_node.next = new_node
    print("New node inserted with data",new_value)
lList = LinkedList()
lList.insertAtBeginning(5)
lList.insertAfter(lList.head,8)
lList.insertAfter(lList.head,3)

Traversing the linked list
In traversing, we visit each node of the linked list to perform some operation on it, for example, printing the data present in each node. Let’s see how we can print all the data values in the linked list.
class Node:
  def __init__(self,data):
    self.data = data
    self.next = None
class LinkedList:
  def __init__(self):
    self.head = None
  def insertAtBeginning(self,new_value):
    new_node = Node(new_value)
    new_node.next = self.head
    self.head = new_node
    print("New node inserted with data",new_value)
  def printLinkedList(self):
    temp = self.head
    while(temp):
      print(temp.data)
      temp = temp.next
lList = LinkedList()
lList.insertAtBeginning(5)
lList.insertAtBeginning(1)
lList.insertAtBeginning(9)
lList.printLinkedList()
Deleting a node from the linked list
Let’s see how we can delete a node from a linked list. This can be done in many ways. But we are not going to try all those different ways.
Here, we will simply create a method that accepts a value and delete the node that contains that value.
class Node:
  def __init__(self,data):
    self.data = data
    self.next = None
class LinkedList:
  def __init__(self):
    self.head = None
  def insertAtBeginning(self,new_value):
    new_node = Node(new_value)
    new_node.next = self.head
    self.head = new_node
    print("New node inserted with data",new_value)
  def printLinkedList(self):
    temp = self.head
    while(temp):
      print(temp.data)
      temp = temp.next
  def deleteNode(self,key):
    temp = self.head
    if(temp==None):
      print("Can't perform deletion")
      return
    while(temp != None):
      if(temp.data == key):
        break
      prev = temp
      temp = temp.next
    prev.next = temp.next
    temp = None
    print("1 node deleted with data",key)
lList = LinkedList()
lList.insertAtBeginning(5)
lList.insertAtBeginning(1)
lList.insertAtBeginning(9)
lList.deleteNode(1)
lList.printLinkedList()

Reversing a linked list
Let’s see the Python code to reverse a linked list.
class Node:
  def __init__(self,data):
    self.data = data
    self.next = None
class LinkedList:
  def __init__(self):
    self.head = None
  def insertAtBeginning(self,new_value):
    new_node = Node(new_value)
    new_node.next = self.head
    self.head = new_node
    print("New node inserted with data",new_value)
  def printLinkedList(self):
    temp = self.head
    while(temp):
      print(temp.data)
      temp = temp.next
  def reverseLinkedList(self):
    prev = None
    curr = self.head
    while(curr is not None):
      next = curr.next
      curr.next = prev
      prev = curr
      curr = next
    self.head = prev
    print("LinkedList reversed")
lList = LinkedList()
lList.insertAtBeginning(5)
lList.insertAtBeginning(1)
lList.insertAtBeginning(9)
lList.printLinkedList()
lList.reverseLinkedList()
lList.printLinkedList()

Deleting a linked list
Now let’s try to write a method that deletes the entire linked list when it gets called.
class Node:
  def __init__(self,data):
    self.data = data
    self.next = None
class LinkedList:
  def __init__(self):
    self.head = None
  def insertAtBeginning(self,new_value):
    new_node = Node(new_value)
    new_node.next = self.head
    self.head = new_node
    print("New node inserted with data",new_value)
  
  def deleteLinkedList(self):
    curr = self.head
    while curr:
      temp = curr.next
      del curr.data
      curr = temp
    print("LinkedList deleted")
lList = LinkedList()
lList.insertAtBeginning(5)
lList.insertAtBeginning(1)
lList.insertAtBeginning(9)
lList.deleteLinkedList()

Circular LinkedlLists
class Node:
  def __init__(self,data):
    self.data = data
    self.next = None
class CircularLinkedList:
  def __init__(self):
    self.head = None
  def push(self,new_value):
    new_node = Node(new_value)
    temp = self.head
    new_node.next = self.head
    if self.head is not None:
      while(temp.next != self.head):
        temp = temp.next
      temp.next = new_node
    else:
      new_node.next = new_node
    self.head = new_node
    print("New node pushed")
  def countNodes(self):
    temp = self.head
    count = 0
    count = count + 1
    while(temp.next != self.head):
      count = count + 1
      temp = temp.next
    print("Count of nodes:",count)
  
  # method to convert a regular linkedlist into circular linkedlist
  def toCircular(self,head):
    start = head
    while(head.next is not None):
      head = head.next
    head.next = start
    return start
  def printCLinkedList(self):
    temp = self.head
    if self.head is not None:
      while(True):
        print(temp.data)
        temp = temp.next
        if(temp == self.head):
          break
cList = CircularLinkedList()
cList.push(5)
cList.push(7)
cList.printCLinkedList()
cList.countNodes()

Doubly Linked Lists
class Node:
  def __init__(self,data):
    self.data = data
    self.next = None
    self.prev = None
class DoublyLinkedList:
  def __init__(self):
    self.head = None
  def push(self,new_value):
    new_node = Node(new_value)
    new_node.next = self.head
    if self.head is not None:
      self.head.prev = new_node
    self.head = new_node
    print("1 node pushed.")
  def insertAfter(self,prev_node,new_data):
    if prev_node is None:
      print("Prev node can't be None")
      return
    new_node = Node(new_data)
    new_node.next = prev_node.next
    prev_node.next = new_node
    if new_node.next is not None:
      new_node.next.prev = new_node
    print("1 node inserted.")
  def insertAtLast(self,new_value):
    new_node = Node(new_value)
    new_node.next = None
    if self.head is None:
      new_node.prev = None
      self.head = new_node
      return
    last_node = self.head
    while(last_node.next is not None):
      last_node = last_node.next
    last_node.next = new_node
    new_node.prev = last_node
    print("1 node inserted at the end.")
  def printNodes(self):
    curr = self.head
    while(curr is not None):
      print(curr.data)
      curr = curr.next
  def deleteNode(self,key):
    if self.head is None or key is None:
      print("Deletion unsuccessful.")
      return
    
dList = DoublyLinkedList()
dList.push(5)
dList.push(6)
dList.insertAfter(dList.head,9)
dList.insertAtLast(3)
dList.printNodes()

I hope this article was helpful to you. Check out my articles on queue and stack data structures in Python.
You can do the LinkedIn concepts visualization here: https://linked-list-visualizer.vercel.app/app
Recent Posts
Modular programming is a software design technique that emphasizes separating the functionality of a program into independent, interchangeable modules. In this tutorial, let's understand what modular...
While Flask provides the essentials to get a web application up and running, it doesn't force anything upon the developer. This means that many features aren't included in the core framework....

