Skip to content

Linked lists

  • No fixed storage, i.e., dynamic memory allocation
  • insertion, deletion takes O(1)
  • It is a linear data structure, which are not stored in a contiguous memory location.

Implementation of Linked List in Python

Typical nodes in linked list looks like:

class Node:
    def __init__(self,data:Any,
                nextNode:Optional[Any]=None) -> None:
        self.data = data # to store the actual data
        self.next = nextNode # pointer to next node

class LinkedList(object):
    def __init__(self,*args):
        self.head = None
        self.tail = None
        self.__size = 0 

    def append(self,data):...
    def pop(self):...
    def insert(self,at,data):...
    def remove(self,index):...
    def __len__(self):...
    def __add__(self):... # I have defined this in such a way that
    #  you can concatenate two list objects with a '+' operator 
    def reverse(self):...

click here to see the code. OR use this to install the addtional data structures

pip install Gsauce-pyds

Example

    from dstructure.linkedlist import LinkedList
    l = LinkedList(range(8))
    print(l)
    # LinkedList([0, 1, 2, 3, 4, 5, 6, 7])

    l.append('last')
    print(l)
    # LinkedList([0, 1, 2, 3, 4, 5, 6, 7, last])

    print(len(l))
    #9

    print(l[0],l[5],l[8])
    # Node(data = 0) Node(data = 5) Node(data = last)

    print(l.head,l.tail)
    # Node(data = 0) Node(data = last)

    l[8]=8
    print(l)
    # LinkedList([0, 1, 2, 3, 4, 5, 6, 7, 8])

    l.reverse()
    print(l,'head: ',l.head,'tail: ',l.tail,sep='\n')
    # LinkedList([8, 7, 6, 5, 4, 3, 2, 1, 0])
    # head: 
    # Node(data = 8)
    # tail: 
    # Node(data = 0)

    print(l[:3],l[3:])
    # LinkedList([8, 7, 6]) LinkedList([5, 4, 3, 2, 1, 0])

    l.insert(0,'zero')
    l.insert(3,'three')
    print(l,len(l),l.head,l.tail,sep='\n')
    # LinkedList([zero, 8, 7, three, 6, 5, 4, 3, 2, 1, 0])
    # 11
    # Node(data = zero)
    # Node(data = 0)

    l.remove(0)
    l.remove(3)
    print(l,l.head,l.tail,sep='\n')
    # LinkedList([8, 7, three, 5, 4, 3, 2, 1, 0])
    # Node(data = 8)
    # Node(data = 0)

    a = LinkedList('a b c'.split())
    print(a)
    a + l  # concatenation will overwrite the original list, in this case it's a
    print(a)
    # LinkedList([a, b, c])
    # LinkedList([a, b, c, 8, 7, three, 5, 4, 3, 2, 1, 0])


    print(len(a))
    a.pop()
    a.pop()
    print(len(a))
    # 12
    # 10

Note

  • Binary search takes O(n)
  • Concatenation takes O(m+n)

Doubly linked list

Linked list but each node contains a pointer to next and previous node.

Doubly linked list node

class node:
    def __init__(self,data,prev=None,next=None):
        self.data = data 
        self.prev = prev
        self.next = next

Doubly linked list class:

class DoublyLinkedList(object):
    def __init__(self,*e):
        self.head = None # reference/pointer to the starting(head) node
        self.tail = None # pointer to the end node
        self.__size =0 # to track the length of the list

    def __len__(self):... # returns the length of the list
    def append(self,data):... # appends a node at the end of the list
    def pop(self):... # removes an element at the end of the list
    def reverse(self):... # reverses the list 

Example

from dstructure.linkedlist import DoublyLinkedList

l = DoublyLinkedList(range(4))
print(l)
# DLList([0 1 2 3])

l.append(4)
print(l,len(l))
# DLList([0 1 2 3 4]) 5

l.pop()
print(l)
# DLList([0 1 2 3])

print(l.head,l[0],l.tail,l[3],sep='\t')
# Node(data = 0)    Node(data = 0)  Node(data = 3)  Node(data = 3)

l.reverse()
print(l.head,l[0],l.tail,l[3],sep='\t')
# Node(data = 3)    Node(data = 3)  Node(data = 0)  Node(data = 0)

print(l.head.next,l.tail.prev)
# Node(data = 2) Node(data = 1)

print(l)
l[2]='two'
print(l)
# DLList([3 2 1 0])
# DLList([3 2 two 0])

print(l[:2],l[2:])
# DLList([3 2]) DLList([two 0])