Monday, March 2, 2020

Introduction to Linked Lists

Introduction to Linked List
----------------------------------------------------------------------

Linked List is one of the many types of Data Structure. In a Linked List, each data record is accompanied with a reference to the next data record, and in certain variations, a reference to the previous data record. A Linked List has the advantage of being able to insert and delete any data record from any location. This makes a Linked List suitable for real-time programs where the amount of data is hard to predict and for programs that require accessing the data records sequentially. However, Linked Lists makes it hard to search for a specific data, as it doesn't allow for random access and has to check each data one-by-one sequentially. It also takes up a large amount of memory.

A Linked List are based around the usage of 'Nodes', which contains a data record and an address pointing to the next node. Some Linked Lists also have an additional address, pointing to the previous node. To determine which Node the program will add a data record to, Linked Lists makes use of a 'Head' address. The Head address usually begins at the first Node before moving to other Nodes. At the end of a Linked List is a 'Null' address to indicate that the Linked List has ended.

Types of Linked List
----------------------------------------------------------------------

Linked Lists have multiple variations, the most popular of them include:

- Singly Linked List
Singly Linked List is the most basic form of a Linked List, and contains only the minimum of what a Linked List has. Within the node, there only exists an address to the next node.

This is how a Singly Linked List node looks like in C:

struct Node { 
int data; 
struct Node* next; 
}; 

Visual example of a Singly Linked List:
- Circular Linked List
Circular Linked List is virtually the same as with a Singly Linked List, but at the end of the Linked List, the Null address is replaced by an address that points to the first Node. This type of Linked List can be used if the program has to cycle through the List multiple times.

Visual example of a Circular Linked List:

- Doubly Linked List
In a Doubly Linked List, the Node does not only contain an address for the next Node, but also an address pointing to the previous Node. This makes it faster to insert and delete Nodes that are located in the middle, but also uses more memory than a Singly Linked List.

This is how a Doubly Linked List node looks like in C:

struct Node { int data; struct Node* next; // Pointer to next node in DLL struct Node* prev; // Pointer to previous node in DLL };

Visual example of a Doubly Linked List:
dll
- Circular Doubly Linked List
Circular Doubly Linked List combines the features of Circular and Doubly Linked List along with the inherent advantages that comes with it, but has the obvious drawback of taking up more memory.

Visual example of a Doubly Linked List:
dll
Insertion and Deletion
----------------------------------------------------------------------

One of the main advantages that a Linked List has to offer is that it can insert and delete Nodes from any location. To better explain this advantage point, we will look at snippets of how insertion and deletion can be done in Singly Linked Lists. These snippets are taken from geeksforgeeks.org

In Linked Lists, insertion is done by allocating the memory for a new Node, putting in the data into the Node and then placing the Node in the required position. Linked List insertion has three main variants, insertion from the front, in the middle, and at the end of the List.

We will begin with insertion from the front. In C, this would look like:

void push(struct Node** head_ref, int new_data) 
/* 1. allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node)); 

/* 2. put in the data */
new_node->data = new_data; 

/* 3. Make next of new node as head */
new_node->next = (*head_ref); 

/* 4. move the head to point to the new node */
(*head_ref) = new_node; 

Now, let us compare with insertion in the middle of the Linked List and at the end of the Linked List. In C, inserting between two Nodes would look like:

void insertAfter(struct Node* prev_node, int new_data) 
/*1. check if the given prev_node is NULL */
if (prev_node == NULL) 
printf("the given previous node cannot be NULL");  
return; 
/* 2. allocate new node */
struct Node* new_node =(struct Node*) malloc(sizeof(struct Node)); 

/* 3. put in the data */
new_node->data = new_data; 

/* 4. Make next of new node as next of prev_node */
new_node->next = prev_node->next; 

/* 5. move the next of prev_node as new_node */
prev_node->next = new_node; 

The main difference between the two is that when inserting in the middle, the program has to check whether the query given for where the new Node would be is Null or not. Other than that, these two are the same.

As for insertion at the end of the List, in C it would look like:

void append(struct Node** head_ref, int new_data) 
/* 1. allocate node */
struct Node* new_node = (struct Node*) malloc(sizeof(struct Node)); 

struct Node *last = *head_ref; /* used in step 5*/

/* 2. put in the data */
new_node->data = new_data; 

/* 3. This new node is going to be the last node, so make next 
of it as NULL*/
new_node->next = NULL; 

/* 4. If the Linked List is empty, then make the new node as head */
if (*head_ref == NULL) 
*head_ref = new_node; 
return; 
/* 5. Else traverse till the last node */
while (last->next != NULL) 
last = last->next; 

/* 6. Change the next of last node */
last->next = new_node; 
return;  

There are a few differences between inserting from the front and at the end. The first is that instead of pointing to the next Node, it instead points at Null. The second is that it has to first search for the last Node before inserting. Step 4 is only there to prevent an error; if this function is used anytime after using the push function, it is not required.

Linked List deletion is done by first searching for the Node that it will delete, unlink the Node and finally free the memory allocation. As for deletion, there are two variations; deleting a Node by its value or by the Node's position. In C, this code can be used to delete a Node by a key value

void deleteNode(struct Node **head_ref, int key)
{
// Store head node
struct Node* temp = *head_ref, *prev;

// If head node itself holds the key to be deleted
if (temp != NULL && temp->data == key)
{
*head_ref = temp->next; // Changed head
free(temp); // free old head
return;
}

// Search for the key to be deleted, keep track of the
// previous node as we need to change 'prev->next'
while (temp != NULL && temp->data != key)
{
prev = temp;
temp = temp->next;
}

// If key was not present in linked list
if (temp == NULL) return;

// Unlink the node from linked list
prev->next = temp->next;

free(temp); // Free memory
}

In comparison, here is what deleting by a Node's position looks like in C:

void deleteNode(struct Node **head_ref, int position)
{
// If linked list is empty
if (*head_ref == NULL)
return;

// Store head node
struct Node* temp = *head_ref;

// If head needs to be removed
if (position == 0)
{
*head_ref = temp->next; // Change head
free(temp); // free old head
return;
}

// Find previous node of the node to be deleted
for (int i=0; temp!=NULL && i<position-1; i++)
temp = temp->next;

// If position is more than number of ndoes
if (temp == NULL || temp->next == NULL)
return;

// Node temp->next is the node to be deleted
// Store pointer to the next of node to be deleted
struct Node *next = temp->next->next;

// Unlink the node from linked list
free(temp->next); // Free memory

temp->next = next; // Unlink the deleted node from list
}

As can be seen, there are not many differences between the two. The biggest difference is in what the code is searching for; a key value or a specific Node.

Closing
----------------------------------------------------------------------

For examples of what a simple, completed program with Linked List looks like, I recommend you to check out the referenced links at the bottom of this post. There are more dedicated sites for educational purposes, and I think having them here will cause the post to go for too long. Regardless, thank you for having spent your time reading this post. I hope that you can help me with my future writing by giving me suggestions for posts like this.

Reference:

No comments:

Post a Comment