Linked Lists Operations

Operations

Linked lists support various operations for manipulating the data they contain. Here are some common operations performed on linked lists:

  1. Traversal

2. Insertion

3. Deletion

4. Sorting

1. Traversal of a linked list

Traversal of a linked list involves visiting each node in the list in order to access or manipulate its data. This process typically requires starting from the head (or start) of the list and moving through each node until the end of the list is reached. Here’s a basic example of how traversal of a singly linked list can be implemented in C:

#include <stdio.h>
#include <stdlib.h>

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

void traverseLinkedList(struct Node* head) {
    struct Node* current = head;
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

int main() {
    struct Node* head = (struct Node*)malloc(sizeof(struct Node));
    struct Node* second = (struct Node*)malloc(sizeof(struct Node));
    struct Node* third = (struct Node*)malloc(sizeof(struct Node));

    head->data = 1;
    head->next = second;
    second->data = 2;
    second->next = third;
    third->data = 3;
    third->next = NULL; // End of the list

    printf("Linked list elements: ");
    traverseLinkedList(head);

    free(head);
    free(second);
    free(third);

    return 0;
}

Output:

Linked list elements: 1 2 3 

2. Delete a Node in a Linked List

Deleting a node in a linked list involves removing a specific node from the list while maintaining the integrity of the list’s structure. There are several cases to consider when deleting a node:

  1. Deleting the head node.
  2. Deleting a node in the middle of the list.
  3. Deleting the last node in the list.

Here’s how you can implement a function to delete a node with a given value from a singly linked list in C:

#include <stdio.h>
#include <stdlib.h>

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

void traverseAndPrint(Node* head) {
    Node* currentNode = head;
    while (currentNode != NULL) {
        printf("%d -> ", currentNode->data);
        currentNode = currentNode->next;
    }
    printf("null\n");
}

Node* deleteSpecificNode(Node* head, Node* nodeToDelete) {
    if (head == nodeToDelete) {
        Node* newHead = head->next;
        free(head);
        return newHead;
    }

    Node* currentNode = head;
    while (currentNode->next && currentNode->next != nodeToDelete) {
        currentNode = currentNode->next;
    }

    if (currentNode->next == NULL) {
        return head;
    }

    Node* temp = currentNode->next;
    currentNode->next = currentNode->next->next;
    free(temp);

    return head;
}

int main() {
    Node* node1 = malloc(sizeof(Node));
    node1->data = 3;
    Node* node2 = malloc(sizeof(Node));
    node2->data = 1;
    Node* node3 = malloc(sizeof(Node));
    node3->data = 6;
    Node* node4 = malloc(sizeof(Node));
    node4->data = 7;
    Node* node5 = malloc(sizeof(Node));
    node5->data = 9;

    node1->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node5;

    printf("Before deletion:\n");
    traverseAndPrint(node1);

    node1 = deleteSpecificNode(node1, node4);

    printf("\nAfter deletion:\n");
    traverseAndPrint(node1);

    free(node1);
    free(node2);
    free(node3);
    free(node5);

    return 0;
}

Output:

Before deletion:
3 -> 1 -> 6 -> 7 -> 9 -> null

After deletion:
3 -> 1 -> 6 -> 9 -> null

3. Insert a Node in a Linked List

To insert a node into a linked list, you typically need to consider a few scenarios:

  1. Insertion at the Beginning: Inserting a node at the beginning of the linked list.
  2. Insertion at the End: Inserting a node at the end of the linked list.
  3. Insertion at a Specific Position: Inserting a node at a specific position within the linked list.

Here’s how you can implement these insertion operations in C:

#include <stdio.h>
#include <stdlib.h>

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

void traverseAndPrint(Node* head) {
    Node* currentNode = head;
    while (currentNode != NULL) {
        printf("%d -> ", currentNode->data);
        currentNode = currentNode->next;
    }
    printf("null\n");
}

Node* insertNodeAtPosition(Node* head, Node* newNode, int position) {
    if (position == 1) {
        newNode->next = head;
        return newNode;
    }

    Node* currentNode = head;
    for (int i = 1; i < position - 1 && currentNode != NULL; i++) {
        currentNode = currentNode->next;
    }

    if (currentNode != NULL) {
        newNode->next = currentNode->next;
        currentNode->next = newNode;
    }
    return head;
}

int main() {
    Node* node1 = malloc(sizeof(Node));
    node1->data = 4;
    
    Node* node2 = malloc(sizeof(Node));
    node2->data = 9;
    
    Node* node3 = malloc(sizeof(Node));
    node3->data = 5;
    
    Node* node4 = malloc(sizeof(Node));
    node4->data = 8;

    node1->next = node2;
    node2->next = node3;
    node3->next = node4;

    printf("Original list:\n");
    traverseAndPrint(node1);

    // Insert a new node with value 97 at position 2
    Node* newNode = malloc(sizeof(Node));
    newNode->data = 70;
    node1 = insertNodeAtPosition(node1, newNode, 2);

    printf("\nAfter insertion:\n");
    traverseAndPrint(node1);

    free(node1);
    free(node2);
    free(node3);
    free(node4);
    free(newNode);

    return 0;
}

Output:

Original list:
4 -> 9 -> 5 -> 8 -> null

After insertion:
4 -> 70 -> 9 -> 5 -> 8 -> null

4.Sorting

Sorting a linked list involves arranging the elements of the list in a specific order, such as ascending or descending order. One common sorting algorithm used for linked lists is Merge Sort, which is efficient for linked lists due to its divide-and-conquer approach.

Here’s how you can implement Merge Sort for sorting a singly linked list in C:

#include <stdio.h>
#include <stdlib.h>

// Define a structure for a node
struct Node {
    int data;
    struct Node* next;
};

// Function to merge two sorted linked lists
struct Node* merge(struct Node* left, struct Node* right) {
    // Base cases
    if (left == NULL) return right;
    if (right == NULL) return left;

    struct Node* result = NULL;

    // Pick either left or right and recur
    if (left->data <= right->data) {
        result = left;
        result->next = merge(left->next, right);
    } else {
        result = right;
        result->next = merge(left, right->next);
    }

    return result;
}

// Function to perform Merge Sort on linked list
void mergeSort(struct Node** head_ref) {
    struct Node* head = *head_ref;
    struct Node* left;
    struct Node* right;

    // Base case: If the list is empty or has only one node
    if (head == NULL || head->next == NULL) {
        return;
    }

    // Split the list into two halves
    struct Node* slow = head;
    struct Node* fast = head->next;

    while (fast != NULL) {
        fast = fast->next;
        if (fast != NULL) {
            slow = slow->next;
            fast = fast->next;
        }
    }

    left = head;
    right = slow->next;
    slow->next = NULL;

    // Recursively sort the two halves
    mergeSort(&left);
    mergeSort(&right);

    // Merge the sorted halves
    *head_ref = merge(left, right);
}

// Function to insert a new node at the beginning of the linked list
void insertAtBeginning(struct Node** head_ref, int new_data) {
    // Allocate memory for a new node
    struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));

    // Assign data to the new node
    new_node->data = new_data;

    // Set the next of the new node to the current head
    new_node->next = *head_ref;

    // Move the head to point to the new node
    *head_ref = new_node;
}

// Function to print the linked list
void printList(struct Node* node) {
    while (node != NULL) {
        printf("%d ", node->data);
        node = node->next;
    }
    printf("\n");
}

// Main function
int main() {
    // Start with an empty linked list
    struct Node* head = NULL;

    // Insert some elements into the linked list
    insertAtBeginning(&head, 3);
    insertAtBeginning(&head, 6);
    insertAtBeginning(&head, 2);
    insertAtBeginning(&head, 9);
    insertAtBeginning(&head, 1);

    // Print the original linked list
    printf("Original linked list: ");
    printList(head);

    // Perform Merge Sort
    mergeSort(&head);

    // Print the sorted linked list
    printf("Sorted linked list: ");
    printList(head);

    return 0;
}

Output:

Original linked list: 1 9 2 6 3 
Sorted linked list: 1 2 3 6 9

Time Complexity of linked list operations

The time complexity of linked list operations varies depending on the specific operation and the size of the linked list. O(n), it does not mean they take the same amount of time.

Linear search for linked lists works the same as for arrays. A list of unsorted values are traversed from the head node until the node with the specific value is found. Time complexity is O(n).

Binary search is not possible for linked lists because the algorithm is based on jumping directly to different array elements, and that is not possible with linked lists.

    Leave a Reply

    Your email address will not be published.

    Need Help?