Linked List

S.1: Linked List

Linked lists are a linear data structure. Each element of a linked list stores a piece of data and a pointer which links to the next node.

Linked list 1

Unlike arrays, stacks, queues and other linear data structures, there is no overarching structure holding the elements of a linked list. Instead, accessing elements of a linked list involves traversing the structure via pointers to the next node starting from the head node. Some operations on linked lists are more efficient than arrays. Adding and deleting elements from an array involves moving each affected element to a new location in memory, which takes O(n) runtime. Inserting and deleting elements in a linked list only requires switching the Next pointers around to accommodate an extra node, which can also be done in O(n) runtime. This can be improved to constant time if the linked list is a Doubly Linked List.

Linked list 2

Linked lists are less efficient than arrays for getting elements by index. They also take up more memory than an array, since each node has to store data and a pointer to the next node. The linked list that we have been discussing so far has been a singly-linked list, meaning it can only be traversed in one direction. Below is a python implementation of a singly-linked list.

python

# Function to initialize the linked list object
class LinkedListNode(object):
    def __init__(self, data):
        self.data = data # Assign data
        self.next = None # Initialize

# To instantiate a simple linked list:
if __name__ == "__main__":
    head = LinkedListNode(1)
    second = LinkedListNode(2)
    third = LinkedListNode(3)
    head.next = second # Link first node with second
    second.next = third
    print(head.data, head.next.data, head.next.next.data)
out: 1, 2, 3

java

// Function to initialize the linked list object
class Node {
    int data;
    Node next;

    Node(int data) {
        this.data = data;
    }
}

In some instances, it can be helpful to have an overarching linked list class to keep the head well-defined. Though the algorithms in this article will be tailored to a node-based approach, an example of the linked list class is given below:

python

class Node:

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


class LinkedList(object):

    def __init__(self):
        self.head = None

if __name__ == '__main__':

    lst = LinkedList()

    lst.head = Node(1)
    second = Node(2)
    third = Node(3)

    llist.head.next = second  # Link first node with second

    second.next = third
    print(lst.head.data, lst.head.next.data, lst.head.next.next.data)
    out: 1, 2, 3

S.2 Doubly Linked Lists and Algorithms

Unlike the singly-linked list, the doubly-linked list contains pointers to the previous node as well as the next node. Doubly-linked lists can be traversed in reverse order, which makes node deletion more efficient. However, it takes more memory to store a pointer to the previous node for every node.

Linked list 3

python

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

java

class Node {
   int data;
   Node next;
   Node prev;

   Node(int data) {
       this.data = data;
   }
}

We will be moving into the essential methods for a linked list class, starting with searching a linked list! For each algorithm in this article, we strongly recommend you try programming each implementation on your own before we show you the solution. Given a linked list head node head and a target value target, return the node with data matching that of the target value.

python

def search(self, target):
        """
        Traverses linked list recursively and returns LinkedList object with the desired target
        if target is not found, None is returned
        """
        #base case
        if self.data == target:
            return self
        #recursive case
        if not self.next:
            return None
        return self.next.search(target)

java

public LinkedListNode search(String target) {
       LinkedListNode current = head;

       while (current != null) {
           if (current.getData() != null && current.getData().equals(target)) {
               return current;
           }
           current = current.getNext();
       }

       return null;
   }
}

If uncomfortable with recursion, here is an iterative implementation:

python

def search(self, target):
        cur = self
        #loops as long as the current node has a data value
        while cur:
            if cur.data == target:
                return cur
            #redefining current node as the next node
            cur = cur.next
        #target was never found
        return None

The next method we will be implementing is the linked list insertion method. Given an existing linked list node a and a new node b to be inserted after a, implement the method a.insert(b) such that a -> b. (Hint: if the pre-existing linked list looks like a -> z, don’t forget to link a -> b -> z)

python

def insert(self, head):
        """
        Given a node head, head is inserted directly after the node that insert is called upon
        ex: node1.insert(node2) resulting linked list: node1 -> node2
        """
        # Making sure there is a node after self
        if self.next:
            head.next = self.next
        # Rerouting pointers
        self.next = head
        head.prev = self
        if head.next:
            head.next.prev = head

java

void add_notes (int data) {
       if (head == null){
           head = new Node(data);
       }
       else{
       Node a_node = head;
       while (a_node.next!=null) {
           a_node = a_node.next;
       }
       a_node.next = new Node(data);
       }

   }

The next method we will be programming is the node deletion method. Given an existing linked list head node head and a value target to be removed from the linked list, successfully delete the first node with a data value matching target from the linked list.

Linked list 4

python

def delete(self, LL):
        """
        Deletes a node with the value of LL from the linked list that the method is called upon
        ex: linked_list = 3 -> 5 -> 2 -> 7
        linked_list.delete(2)
        linked_list = 3 -> 5 -> 7
        no return value
        """
        pointer = self.search(LL)
        #if element is not found in list
        if pointer is None:
            return None
        #making sure element is not first
        if pointer.prev:
            #redefining where each node points in order to remove node from the linked list
            pointer.prev.next = pointer.next
            if pointer.next:
                pointer.next.prev = pointer.prev
        else:
            """
            This means that the first element is the one being deleted. In this case, we will instead delete
            the second node and set the first node's data equal to the second's. This allows the user to
            continue to call the linked list by the head node (in case they don't have access to the second node)
            """
            self.data = self.next.data
            if self.next.next:
                self.next.next.prev = self
                self.next = self.next.next

java

void delete (int data) {
       Node random_node=head;
       if (random_node.data==data){
           head=head.next;
       }
       else{
            while(random_node.next.data!=data&&random_node.next!=null){
                random_node=random_node.next;
            }
            if(random_node.next!=null){
                random_node.next=random_node.next.next;
            }
       }
}

Finally, it would be nice to have a way to check whether we have made the correct changes to our linked list after each operation. Create a way to print out the contents of a linked list given a head node head.

python

def print_list(self):
cur = self
while cur:
print(cur.data)
cur = cur.next

java

void int print(Node head) {
     if (head==null) {
           return length;
       }
       else{
           Node my_node=head;
           while (my_node.next!=null){
               System.out.println(my_node.data);
               my_node=my_node.next;
           }
       }
   }

Exercises

Practice problems: By now, you should be able to create your own Linked List. Code your own linked list, along with the various methods we went over: insert, delete, search. You may have noticed that the above insert method requires the user to have access to the existing node that the new node is inserted after. When working with linked lists, the user may not always have this node, especially if inserting into the middle of a linked list. Thus, how would you write an insert_at_index method that takes an extra parameter index which denotes the location to insert the new node? What about an insert_at_value method that inserts the new node after a given value is found in the linked list? The deletion method we implemented is essentially a delete_at_value method. Try implementing a delete_at_index method instead.

What does this function do?

python

def fun1(head):
    if(head == None):
        return
    fun1(head.next)
    print(head.data, end = " ")
static void fun1(Node head){
    if (head == null){
        return;
    }

    fun1(head.next);
    System.out.print(head.data + " ");
}

//Answer: Prints out the data from each Node in a given linked list

https://practice.geeksforgeeks.org/explore?page=1&category[]=Linked%20List&sortBy=submissions


Additional Resources

Linked List

Video We Made to Help Explain Linked List:

More linked list problems:

https://practice.geeksforgeeks.org/explore?page=1&category[]=Linked%20List&sortBy=submissions


Sources

https://www.geeksforgeeks.org/what-is-linked-list/

PA DSA

Introduction to Data Structures and Algorithms
  • Thomas H. Cormen
  • Charles E. Leiserson
  • Ronald L. Rivest
  • Clifford Stein
Copyright CSC630 - 2022©

MIT License

Notice

PA DSA is optimized for learning, testing, and training. Examples might be simplified to improve reading and basic understanding. Tutorials, references, and examples are constantly reviewed to avoid errors, but we cannot warrant full correctness of all content.

Contact

csc630@gmail.com