Linked List#


Contents#


Linked List#

A linked list is a dynamic linear data structure; dynamic, meaning that its size changes at runtime; and linear, meaning that data items are arranged linearly/sequentially.

Insertions and deletions are made anywhere in the linked list.

Implementations#

C#

The null pointer indicates the end of the data structure.

Requires dynamic memory allocation.

Self-referential data items

struct node {
  int data;             // names the data item
  struct node *nxt_ptr; // points to a structure of type `struct node`
};

or

typedef struct node Node; // `Node` is an alias of `struct node`
        struct node {
  int data;               // names the data item
  Node *nxt_ptr;          // points to a structure of type `Node`
}
new_ptr = (Node *)malloc(sizeof(Node));
//        ^^^^^^^^ `malloc` returns type `void *`

Linked List#

Insert node

  • at the front of the linked list

  • after a node

  • after the last node (at the end of the linked list)

Traversal begins at the head node and stops when null is reached.

class Node:
  def __init__ (self, value):
    self.value = value
    self.next  = None

class LinkedList:
  def __init__ (self):
    self.head = None

  def print_list (self):
    temp = self.head
    while (temp):
      print(temp.value, end=' ')
      temp = temp.next
  
  def add_first (self, e):
    new_node      = Node(e)
    new_node.next = self.head # point the new node to the current head
    self.head     = new_node  # make  the new node    the     new head

  def add_last (self, e):
    new_node = Node(e)     # create a new node with value `e`
    if self.head is None:  # if the linked list is empty, assign the new node to the head
      self.head = new_node
      return
    last = self.head       # if the linked list is not empty, start at the current head and traverse the linked list
    while (last.next):
      last = last.next
    last.next = new_node   # point the last node to the new node

  def delete_first (self):
    if self.head is None:
      print('the list is empty')
      return
    self.head = self.head.next

  def delete_node (self, e):
    temp = self.head               # store the current head
    if temp is not None:           # if    the current head is to be deleted...
      if temp.value == e:
        self.head = self.head.next # ...point head to the next node
        temp      = None
        return
    while temp is not None and temp.value != e: # traverse to the last node or node e
      prev = temp
      temp = temp.next
    if temp == None: # if the last node has been reached, the node e does not exist
      return

    # otherwise, node e has been found
    prev.next = temp.next # link the previous node to the next node
    temp.next = None
    temp      = None


linked_list = LinkedList()

# create nodes
linked_list.head = Node(2)
node2 = Node(4)
node3 = Node(6)
node4 = Node(8)

# link nodes
linked_list.head.next = node2
node2.next = node3
node3.next = node4

linked_list.print_list()
2 4 6 8 

Circular Linked List#

A circular linked list is just a linked list in which the last node points to the first node rather than null. A circular linked list can be singly linked or doubly linked.

A circular linked list is traversed by starting at a node and ending at the first node that was already visited (i.e., the starting node).

Advantages

  • any node can serve as the starting point, and the entire list can be traversed starting from any node

  • useful for the implementation of a queue: maintain a pointer to the last inserted node, and the front of the list is obtained by taking the next node

  • circular linked lists are useful for applications which make multiple passes around the list

class Node:
  def __init__ (self, data):
    self.data = data
    self.next = None # pointer to the next node

class Circular_Linked_List:
  def __init__ (self):
    self.head = None

  def prepend_node (self, data):
    new_node = Node(data)               # generate the new node
    tmp_node = self.head                # store the current head node
    new_node.next = self.head           # point the new node to the current head node (which may or may not be empty)
    if self.head is not None:           # if the current list contains at least one node...
      while tmp_node.next != self.head: # ...traverse the list up to the node prior to the current head node
        tmp_node = tmp_node.next        #
      tmp_node.next = new_node          # ...and link this node to the current head node 
    else:
      new_node.next = new_node          # if the list is empty, link the new node to itself
    self.head = new_node                # the new node is the new head node
  
  def print_list (self):
    if self.head is not None:
      temp = self.head
      end  = False
      while not end:                    # foreach node, starting with the head node
        print(temp.data, end=' ')       #     print the node
        temp = temp.next                #     point to the next node
        if temp == self.head:           #     if the next node is the head node
          end = True                    #         stop iterating over nodes

circular_linked_list = Circular_Linked_List()
circular_linked_list.prepend_node(8)
circular_linked_list.prepend_node(6)
circular_linked_list.prepend_node(4)
circular_linked_list.prepend_node(2)
circular_linked_list.print_list()
2 4 6 8 

Doubly Linked List#

A doubly linked list contains a second prev (“previous”) pointer in addition to the usual next pointer.

Advantages

  • a doubly linked list can be traversed in both the forward direction and the backward direction

  • the delete operation is efficient since each node has a pointer to its previous node (in a singly linked list, the list may need to be traversed to obtain a pointer to the previous node of a node to be deleted)

Disadvantages

  • the extra space requirement for the additional pointer


https://www.geeksforgeeks.org/given-a-linked-list-which-is-sorted-how-will-you-insert-in-sorted-way/