Day 10 (Linked Lists Basics)

Day 10 (Linked Lists Basics)

Today's focus was on mastering the fundamentals of linked lists. I worked on implementing a singly linked list with essential operations like insertion, deletion, and traversal. Additionally, I solved problems such as reversing a linked list, detecting cycles using Floyd’s Cycle Detection algorithm, merging two sorted linked lists, and finding the middle element of a linked list. These exercises were instrumental in strengthening my foundation in linked list operations.

Check out my progress in my GitHub repository: github.com/AayJayTee/100-Days-DSA.


Q1. Implement a singly linked list with operations for insertion, deletion, and traversal.

#include <iostream>
using namespace std;

// Class representing a node in the linked list
class node {
    public:
    int data;     // Data stored in the node
    node* next;   // Pointer to the next node

    // Constructor to initialize a node with a value
    node(int val) {
        data = val;
        next = NULL;
    }
};

// Function to insert a node at the end of the linked list
void insertNodeatTail(node* &head, int val) {
    node* n = new node(val);  // Create a new node

    if (head == NULL) {       // If list is empty, make new node the head
        head = n;
        return;
    }

    node* temp = head;        // Traverse to the end of the list
    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = n;           // Link the last node to the new node
}

// Function to insert a node at the beginning of the linked list
void insertNodeatHead(node* &head, int val) {
    node* n = new node(val);  // Create a new node
    n->next = head;           // Link the new node to the current head
    head = n;                 // Update head to the new node
}

// Function to insert a node at a specific position
void insertNode(node* &head, int val, int pos) {
    node* n = new node(val);  // Create a new node
    node* temp = head;
    for (int i = 0; i < pos - 1; i++) {  // Traverse to the desired position
        temp = temp->next;
    }
    n->next = temp->next;     // Link the new node to the next node
    temp->next = n;           // Link the previous node to the new node
}

// Function to delete a node from the end of the linked list
void deleteNodeatTail(node* &head) {
    if (head == NULL) {       // If the list is empty, do nothing
        return;
    }

    if (head->next == NULL) { // If only one node exists
        delete head;          // Delete it and set head to NULL
        head = NULL;
        return;
    }

    node* temp = head;        // Traverse to the second last node
    while (temp->next->next != NULL) {
        temp = temp->next;
    }
    node* to_delete = temp->next;  // Save the last node
    temp->next = NULL;        // Unlink the last node
    delete to_delete;         // Delete the last node
}

// Function to delete a node from the beginning of the linked list
void deleteNodeatHead(node* &head) {
    node* to_delete = head;   // Save the current head
    head = head->next;        // Move head to the next node
    delete to_delete;         // Delete the old head
}

// Function to delete a node by value
void deleteNode(node* &head, int val) {
    if (head == NULL) {       // If the list is empty, do nothing
        cout << "List is empty.\n";
        return;
    }

    if (head->data == val) {  // If the value is in the head node
        deleteNodeatHead(head);
        return;
    }

    node* temp = head;
    while (temp->next->data != val) {  // Traverse to the node before the target node
        temp = temp->next;
    }
    if (temp->next == NULL) { // If the value is not found
        cout << "Value not found.\n";
        return;
    }
    node* to_delete = temp->next;  // Save the target node
    temp->next = temp->next->next; // Unlink the target node
    delete to_delete;              // Delete the target node
}

// Function to display the linked list
void display_linked_list(node* &head) {
    cout<<"The linked list structure is: ";
    node* temp = head;
    while (temp != NULL) {     // Traverse through the list
        cout << temp->data << " "; // Print each node's data
        temp = temp->next;
    }
}

int main() {
    node* head = NULL;         // Initialize the linked list

    insertNodeatTail(head, 1); // Insert nodes at the tail
    insertNodeatTail(head, 2);
    insertNodeatTail(head, 3);
    display_linked_list(head); // Display the list
    cout << endl;

    insertNodeatHead(head, 4); // Insert a node at the head
    display_linked_list(head);
    cout << endl;

    insertNode(head, 5, 3);    // Insert a node at position 3
    display_linked_list(head);
    cout << endl;

    deleteNode(head, 2);       // Delete the node with value 2
    display_linked_list(head);
    cout << endl;

    deleteNodeatHead(head);    // Delete the head node
    display_linked_list(head);
    cout << endl;

    deleteNodeatTail(head);    // Delete the tail node
    display_linked_list(head);
    return 0;
}

The provided code implements a singly linked list with operations for insertion, deletion, and traversal. The node class represents a node in the linked list, storing an integer data and a pointer next to the subsequent node. The insertion functions include adding a node at the tail (insertNodeatTail), at the head (insertNodeatHead), and at a specific position (insertNode). The deletion functions allow removing a node at the head (deleteNodeatHead), at the tail (deleteNodeatTail), or by a specified value (deleteNode). The display_linked_list function traverses the list and prints the values of each node. The main function demonstrates these operations, starting with an empty list, adding nodes in different positions, removing nodes based on conditions, and displaying the linked list after each operation. This program effectively showcases the fundamental operations of a singly linked list and their implementation in C++.


Q2. Write a program to reverse a linked list.

#include <iostream>
using namespace std;

// Class representing a node in the linked list
class node {
    public:
    int data;     // Data stored in the node
    node* next;   // Pointer to the next node

    // Constructor to initialize a node with a value
    node(int val) {
        data = val;
        next = NULL;
    }
};

// Function to insert a node at the end of the linked list
void insertNodeatTail(node* &head, int val) {
    node* n = new node(val);  // Create a new node

    if (head == NULL) {       // If list is empty, make new node the head
        head = n;
        return;
    }

    node* temp = head;        // Traverse to the end of the list
    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = n;           // Link the last node to the new node
}

// Function to display the linked list
void display_linked_list(node* &head) {
    cout<<"The linked list structure is: ";
    node* temp = head;
    while (temp != NULL) {     // Traverse through the list
        cout << temp->data << " "; // Print each node's data
        temp = temp->next;
    }
}

void reverse_linked_list(node* &head){
    node* prev_node = NULL;              // Previous node pointer initialized to NULL
    node* current_node = head;           // Current node starts at the head
    node* next_node;                     // Pointer to store the next node

    while (current_node != NULL) {
        next_node = current_node->next;  // Store the next node
        current_node->next = prev_node;  // Reverse the current node's pointer
        prev_node = current_node;        // Move the previous pointer to the current node
        current_node = next_node;        // Move the current pointer to the next node
    }
    head = prev_node;                    // Update the head to the new first node
}

int main() {
    node* head = NULL;                  // Initialize the linked list
    insertNodeatTail(head, 1);          // Insert nodes at the tail
    insertNodeatTail(head, 2);
    insertNodeatTail(head, 3);
    insertNodeatTail(head, 4);
    display_linked_list(head);
    cout<<endl;
    reverse_linked_list(head);
    display_linked_list(head);
}

The code implements a singly linked list with functionality to reverse it. The node class represents a single node in the list, containing integer data and a pointer next. The insertNodeatTail function adds nodes at the end of the list, and display_linked_list traverses and prints the entire list. The reverse_linked_list function reverses the linked list iteratively. It uses three pointers: prev_node to track the previous node (initially NULL), current_node for the current node (starting at the head), and next_node to store the next node temporarily. By iterating through the list, it reverses the direction of each node's next pointer and updates the head to the last node after traversal. The main function demonstrates this functionality by creating a linked list, displaying it, reversing it, and displaying the reversed list.


Q3. Solve the "detect a cycle in a linked list" problem using Floyd’s Cycle Detection algorithm.

#include <iostream>
using namespace std;

// Class representing a node in the linked list
class node {
    public:
    int data;     // Data stored in the node
    node* next;   // Pointer to the next node

    // Constructor to initialize a node with a value
    node(int val) {
        data = val;
        next = NULL;
    }
};

// Function to insert a node at the end of the linked list
void insertNodeatTail(node* &head, int val) {
    node* n = new node(val);  // Create a new node

    if (head == NULL) {       // If list is empty, make new node the head
        head = n;
        return;
    }

    node* temp = head;        // Traverse to the end of the list
    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = n;           // Link the last node to the new node
}

// Function to display the linked list
void display_linked_list(node* &head) {
    cout<<"The linked list structure is: ";
    node* temp = head;
    while (temp != NULL) {     // Traverse through the list
        cout << temp->data << " "; // Print each node's data
        temp = temp->next;
    }
}

//Function to detect if cycle is present
bool isCyclePresent(node* &head){
    node* fast = head;   // Fast pointer starts at the head
    node* slow = head;   // Slow pointer starts at the head

    while(fast != NULL && fast->next != NULL){  // Loop until fast pointer reaches the end
        fast = fast->next->next;  // Move fast pointer two steps ahead
        slow = slow->next;        // Move slow pointer one step ahead

        if(fast == slow){  // If they meet, a cycle is present
            return true;
        }
    }
    return false;  // No cycle found
}


// Function to create a cycle in the linked list for testing
void createCycle(node* &head) {
    if (head == NULL) return;

    node* temp = head;
    while (temp->next != NULL) {
        temp = temp->next;  // Traverse to the last node
    }

    // Create a cycle by linking the last node to a node (e.g., the second node)
    temp->next = head->next;  // Create a cycle by making the last node point to the second node
}

int main() {
    node* head = NULL;                  // Initialize the linked list
    insertNodeatTail(head, 1);          // Insert nodes at the tail
    insertNodeatTail(head, 2);
    insertNodeatTail(head, 3);
    insertNodeatTail(head, 4);
    display_linked_list(head);
    cout<<endl;
    if(isCyclePresent(head)){
        cout<<"Cycle is present";
    }
    else{
        cout<<"Cycle is not present";
    }
    createCycle(head);
    cout<<endl;
    cout<<"Cycle has been created";
    cout<<endl;
    if(isCyclePresent(head)){           //Using the function to create a cycle
        cout<<"Cycle is present";
    }
    else
        cout<<"Cycle is not present";
}

The program demonstrates how to work with a singly linked list, including functionality to detect and create cycles. The node class represents each element of the list, containing data and a pointer to the next node. The insertNodeatTail function appends nodes to the list, and display_linked_list prints the elements in order.

The isCyclePresent function detects whether a cycle exists in the list using the Floyd’s Cycle Detection Algorithm (tortoise and hare approach). Two pointers, slow and fast, traverse the list at different speeds. If they meet, a cycle exists; otherwise, the traversal ends when fast reaches NULL.

The createCycle function artificially creates a cycle by linking the last node to a middle node. In main, a list is constructed, displayed, and tested for cycles. Initially, no cycle exists. After invoking createCycle, a cycle is created, and the program verifies its presence using isCyclePresent. The output illustrates both scenarios, demonstrating the linked list operations and cycle detection effectively.


Q4. Write a program to merge two sorted linked lists.

#include <iostream>
using namespace std;

// Class representing a node in the linked list
class node {
    public:
    int data;     // Data stored in the node
    node* next;   // Pointer to the next node

    // Constructor to initialize a node with a value
    node(int val) {
        data = val;
        next = NULL;
    }
};

// Function to insert a node at the end of the linked list
void insertNodeatTail(node* &head, int val) {
    node* n = new node(val);  // Create a new node

    if (head == NULL) {       // If list is empty, make new node the head
        head = n;
        return;
    }

    node* temp = head;        // Traverse to the end of the list
    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = n;           // Link the last node to the new node
}

// Function to display the linked list
void display_linked_list(node* &head) {
    cout<<"The linked list structure is: ";
    node* temp = head;
    while (temp != NULL) {     // Traverse through the list
        cout << temp->data << " "; // Print each node's data
        temp = temp->next;
    }
}

void merge_linked_lists(node* &head1, node* &head2) {
    // If the first list is empty, point it to the second list
    if (head1 == NULL) {
        head1 = head2;
    }

    // If the second list is empty, there's nothing to merge, so return
    if (head2 == NULL) {
        return;
    }

    // Traverse to the end of the first linked list
    node* temp = head1;
    while (temp->next != NULL) {
        temp = temp->next;
    }

    // Link the last node of the first list to the head of the second list
    temp->next = head2;
}


int main() {
    node* head1 = NULL;                  // Initialize the linked list
    insertNodeatTail(head1, 1);          // Insert nodes at the tail
    insertNodeatTail(head1, 2);
    insertNodeatTail(head1, 3);
    insertNodeatTail(head1, 4);
    display_linked_list(head1);
    cout<<endl;
    node* head2 = NULL;
    insertNodeatTail(head2, 5);          // Insert nodes at the tail
    insertNodeatTail(head2, 6);
    insertNodeatTail(head2, 7);
    insertNodeatTail(head2, 8);
    display_linked_list(head2);
    cout<<endl;
    merge_linked_lists(head1,head2);
    display_linked_list(head1);
}

This program demonstrates the creation, traversal, and merging of two singly linked lists. A class node is defined to represent each node in the linked list, storing data and a pointer to the next node. The insertNodeatTail function inserts a new node with a given value at the end of the linked list. The display_linked_list function traverses the list, printing the data in each node.

The merge_linked_lists function combines two linked lists. If the first list (head1) is empty, it points to the second list (head2). If the second list is empty, no changes are made. Otherwise, the function traverses to the last node of the first list and links it to the head of the second list, effectively merging the two lists.

In the main function, two linked lists (head1 and head2) are created and populated with nodes using insertNodeatTail. Both lists are displayed individually using display_linked_list. Then, merge_linked_lists is called to merge the two lists, and the resulting merged list is displayed.


Q5. Solve the "find the middle element of a linked list" problem.

#include <iostream>
using namespace std;

// Class representing a node in the linked list
class node {
    public:
    int data;     // Data stored in the node
    node* next;   // Pointer to the next node

    // Constructor to initialize a node with a value
    node(int val) {
        data = val;
        next = NULL;
    }
};

// Function to insert a node at the end of the linked list
void insertNodeatTail(node* &head, int val) {
    node* n = new node(val);  // Create a new node

    if (head == NULL) {       // If list is empty, make new node the head
        head = n;
        return;
    }

    node* temp = head;        // Traverse to the end of the list
    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = n;           // Link the last node to the new node
}

void findMiddle(node* &head){
    node* fast=head;
    node* slow=head;
    while(fast!=NULL && fast->next!=NULL){
        fast=fast->next->next;
        slow=slow->next;
    }
    cout<<"The middle element is: ";
    cout<<slow->data;
}

// Function to display the linked list
void display_linked_list(node* &head) {
    cout<<"The linked list structure is: ";
    node* temp = head;
    while (temp != NULL) {     // Traverse through the list
        cout << temp->data << " "; // Print each node's data
        temp = temp->next;
    }
}

int main() {
    node* head = NULL;                  // Initialize the linked list
    insertNodeatTail(head, 1);          // Insert nodes at the tail
    insertNodeatTail(head, 2);
    insertNodeatTail(head, 3);
    insertNodeatTail(head, 4);
    insertNodeatTail(head, 5);
    insertNodeatTail(head, 6);
    insertNodeatTail(head, 7);
    display_linked_list(head);
    cout<<endl;
    findMiddle(head);
}

This program identifies the middle element of a singly linked list. The class node represents each node in the linked list, with properties for storing data and pointing to the next node. The insertNodeatTail function appends a new node with a given value to the end of the list, while the display_linked_list function traverses and prints all elements of the list.

The findMiddle function determines the middle element using the two-pointer approach. It initializes two pointers: slow (which moves one step at a time) and fast (which moves two steps at a time). When the fast pointer reaches the end of the list, the slow pointer will be at the middle element. The value of the middle node is then printed.

In the main function, a linked list is created and populated with seven nodes using insertNodeatTail. The list is displayed using display_linked_list. The findMiddle function is then called to calculate and display the middle element of the list.


Today's practice significantly enhanced my understanding of linked lists. Implementing core operations, detecting cycles, and solving practical problems like merging sorted lists and finding the middle element provided a comprehensive learning experience. Excited to delve deeper into more advanced linked list problems in the coming days. Onward to Day 11!