11. DSA with C++ - Doubly Linked List

Doubly Linked List

A doubly linked list is a linear data structure that consists of a sequence of nodes. Each node contains data and two pointers, one pointing to the previous node and another pointing to the next node in the list. This allows for traversal in both directions, forward and backward.

A step-by-step explanation and implementation of a doubly linked list in C++ with various operations:

#include <iostream>
struct Node {
    int data;
    Node* prev;
    Node* next;
};

class DoublyLinkedList {
    private:
    Node* head;
    Node* tail;
    public:
    DoublyLinkedList() {
          head = nullptr;
          tail = nullptr;
    }
    // Operations...
};
  
Step 1: Define the Node Structure

    struct Node {
      int data;
      Node* prev;
      Node* next;
    };
  
Step 2: Implement the DoublyLinkedList Class

    class DoublyLinkedList {
    private:
      Node* head;
      Node* tail;
    public:
      DoublyLinkedList() {
        head = nullptr;
        tail = nullptr;
      }
      // Operations...
    };
  
Step 3: Implement the Insertion Operations

    //insert at the beginning position of DoublyLinkedList

    void DoublyLinkedList::insertAtBeginning(int value) {
      Node* newNode = new Node;
      newNode->data = value;
      newNode->prev = nullptr;
      newNode->next = head;
  
      if (head != nullptr)
          head->prev = newNode;
      else
          tail = newNode;
  
      head = newNode;
  
    }

    //insert at the end position of DoublyLinkedList

    void DoublyLinkedList::insertAtEnd(int value) {
      Node* newNode = new Node;
      newNode->data = value;
      newNode->next = nullptr;
      newNode->prev = tail;
  
      if (tail != nullptr)
          tail->next = newNode;
      else
          head = newNode;
  
      tail = newNode;
  
    }

    //insert at the speicifiec position of DoublyLinkedList
    
    void DoublyLinkedList::insertAtPosition(int value, int position) {
      if (position < 1) {
        std::cout << "Invalid position." << std::endl;
        return;
    }

    if (position == 1) {
        insertAtBeginning(value);
        return;
    }

    Node* newNode = new Node;
    newNode->data = value;

    Node* current = head;
    int currentPosition = 1;

    while (current != nullptr && currentPosition < position - 1) {
        current = current->next;
        currentPosition++;
    }

    if (current == nullptr) {
        std::cout << "Invalid position." << std::endl;
        return;
    }

    newNode->prev = current;
    newNode->next = current->next;

    if (current->next != nullptr)
        current->next->prev = newNode;
    else
        tail = newNode;

    current->next = newNode;

  }
  
Step 4: Implement the Deletion Operations


    //delete a node from beginning of the DoublyLinkedList

    void DoublyLinkedList::deleteAtBeginning() {
      if (head == nullptr) {
        std::cout << "The list is empty." << std::endl;
        return;
      }

      Node* temp = head;
      head = head->next;

      if (head != nullptr)
          head->prev = nullptr;
      else
          tail = nullptr;

      delete temp;

    }

    //delete a node from end of the DoublyLinkedList
  
    void DoublyLinkedList::deleteAtEnd() {
      if (tail == nullptr) {
        std::cout << "The list is empty." << std::endl;
        return;
      }

      Node* temp = tail;
      tail = tail->prev;

      if (tail != nullptr)
          tail->next = nullptr;
      else
        head = nullptr;

      delete temp;
    }

    //delete a node from a specifiec position of the DoublyLinkedList

    void DoublyLinkedList::deleteAtPosition(int position) {
      if (position < 1 || head ==

      nullptr) {
              std::cout << "Invalid position or the list is empty." << std::endl;
              return;
          }
      
          if (position == 1) {
              deleteAtBeginning();
              return;
          }
      
          Node* current = head;
          int currentPosition = 1;
      
          while (current != nullptr && currentPosition < position) {
              current = current->next;
              currentPosition++;
          }
      
          if (current == nullptr) {
              std::cout << "Invalid position." << std::endl;
              return;
          }
      
          current->prev->next = current->next;
      
          if (current->next != nullptr)
              current->next->prev = current->prev;
          else
              tail = current->prev;
      
          delete current;
      
    }
  
Step 5: Implement Display Operations


    //display the DoublyLinkedList element forward

    void DoublyLinkedList::displayForward() {
      if (head == nullptr) {
        std::cout << "The list is empty." << std::endl;
        return;
      }

      Node* current = head;

      while (current != nullptr) {
          std::cout << current->data << " ";
          current = current->next;
      }

      std::cout << std::endl;
  
    }

    //display the DoublyLinkedList element backward

    void DoublyLinkedList::displayBackward() {
      void DoublyLinkedList::displayBackward() {
        if (tail == nullptr) {
            std::cout << "The list is empty." << std::endl;
            return;
        }
    
        Node* current = tail;
    
        while (current != nullptr) {
            std::cout << current->data << " ";
            current = current->prev;
        }
    
        std::cout << std::endl;      
    }
  
Step 6: Test the Doubly Linked List

    int main() {
      DoublyLinkedList list;
      list.insertAtBeginning(10);
      list.insertAtEnd(20);
      list.insertAtEnd(30);
      list.insertAtPosition(15, 2);
      list.displayForward(); // Output: 10 15 20 30
      list.displayBackward(); // Output: 30 20 15 10
      list.deleteAtPosition(3);
      list.displayForward(); // Output: 10 15 30
      list.displayBackward(); // Output: 30 15 10
      return 0;
    }
  
In this implementation, we define a Node structure to represent the nodes in the doubly linked list. The DoublyLinkedList class is implemented with member functions to perform various operations on the list.

We have implemented insertion operations such as insertAtBeginning, insertAtEnd, and insertAtPosition. Deletion operations include deleteAtBeginning, deleteAtEnd, and deleteAtPosition. Display operations displayForward and displayBackward are also implemented to print the list in both directions.

In the main function, we create a DoublyLinkedList object, perform operations on it, and display the list at various stages.

When you run the code, the output will be:

  forward:   10 15 20 30
  backward:  30 20 15 10
  
  forward:   10 15 30
  backward:  30 15 10