Home > Software design >  Can we reverse a doubly linked list by traversing it till the end and setting last pointer to head?
Can we reverse a doubly linked list by traversing it till the end and setting last pointer to head?

Time:02-03

Instead of actually reversing the link between nodes, I have traversed the list till the very end and set the last node address as head. I prepared a separate function for printing it in specific order which works well except that its returning garbage value for the last node. Here's the code -

void reverselist(Node * &head){
    Node *temp =head;
    Node *prevtemp = NULL;
    while(temp !=NULL){
        prevtemp = temp;
        temp = temp->next;
    }
    head = prevtemp;
}
void printreverse(Node * &head){
    Node *temp = head;
    while(temp!=NULL){
        cout << temp->data << " ";
        temp = temp->prev;
    }
}
int main() {
    Node * node1= new Node(54);
    Node *head = node1;
    Node *tail =node1;
    insertathead(head, tail, 10);
    printlist(head);
    insertattail(tail, 23);
    printlist(head);
    insertatanyposition(head, tail,43,2);
    printlist(head);  
    reverselist(head);
    printreverse(head);
    return 0;
}

CodePudding user response:

The answer is no. This does not work.

I also do not see a double linked list. Looks more like you wanted to implement a singly linked list.

Additionally, I do not see a linked list somewhere in the code. Maybe you mix up "Node" and "Linked List". The "Node" is usually a part of the linked list. So it is contained in the Linked List class. And the whole functionality of the "Node" is usually not exposed to the outside world.

Example with Singly Linked list and reverse iterator in the function main at the bottome:

#include <iostream>
#include <iterator>
#include <initializer_list>
#include <algorithm>

// Very simple implementation of a forward list
template <class T>
class SinglyLinkedList {

    // The node
    struct Node {
        T data{};     // Data. Would normally be a templated argument
        Node* next{};   // And the pointer to the next node

        Node(const T& i, Node* n = nullptr) : data(i), next(n) {}; // Simple constructor to set a value and next pointer
    };

    Node* head{};       // This is the start of the list
    // It would be advisable to have a tail pointer. We use the more inefficient approach here
    Node* getLast() const { Node* n{ head }; while (n and n->next) n = n->next; return n; }

public:
    // Constructor / Destructor --------------------------------------------------------------------------------------------------------
    ~SinglyLinkedList() { clear(); }

    // Default constuctor
    SinglyLinkedList() {}   // Default

    // From an initialization list
    SinglyLinkedList(const std::initializer_list<T>& il) { clear();  for (const T& i : il) push_back(i); } // From initializer list

    // Copy constructor
    SinglyLinkedList(const SinglyLinkedList& other) { clear(); for (const T& i : other) push_back(i); }

    // Move constructor. Will steal the elements from the other 
    SinglyLinkedList(SinglyLinkedList&& other) noexcept { head = other.head; other.head = nullptr; }

    // Assignment operator
    SinglyLinkedList& operator = (const SinglyLinkedList& other) { clear(); for (const T& i : other) push_back(i); }

    // Move assignment operator 
    SinglyLinkedList& operator = (SinglyLinkedList&& other) { head = other.head; other.head = nullptr; }

    // Housekeeping --------------------------------------------------------------------------------------------------------------
    void clear() { Node* tmp{ head }; while (tmp) { Node* toDelete{ tmp }; tmp = tmp->next; delete toDelete; } head = nullptr; }
    int empty() const { return head == nullptr; }
    int size() const { int k{}; Node* n{ head }; while (n) {   k; n = n->next; } return k; }

    // Modify content --------------------------------------------------------------------------------------------------------------
    void push_front(const T& i) { Node* n = new Node(i); n->next = head; head = n; };
    void push_back(const T& i) { Node* n = new Node(i); Node* l = getLast(); if (l) l->next = n; else head = n; }
    void pop_front() { if (head) { Node* tmp = head->next; delete head; head = tmp; } }
    void pop_back() { // This is a little bit more difficult in a singly linked list
        if (head) {
            Node* n{ head }, * previous{};
            while (n and n->next) {
                previous = n;
                n = n->next;
            }
            delete n;
            if (previous)
                previous->next = nullptr;
            else
                head->next = nullptr;
        }
    }

    // Access elements --------------------------------------------------------------------------------
    T& front() const { return head ? head->data : 0; };
    T back() const { Node* n = getLast(); return n ? n->data : 0; }

    // Add iterator properties to class ---------------------------------------------------------------
    struct iterator {                           // Local class for iterator
        Node* iter{};                           // Iterator is basically a pointer to the node
        Node* head{};

        // Define alias names necessary for the iterator functionality
        using iterator_category = std::bidirectional_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = T;
        using pointer = T*;
        using reference = T&;

        // Constructor
        iterator() {}
        iterator(Node* n, Node* h) : iter(n), head(h) {}

        // Dereferencing
        reference operator *() const { return iter->data; }
        pointer operator ->() const { return &iter->data; }

        // Aithmetic operations
        iterator& operator   () { if (iter) iter = iter->next; return *this; }
        iterator operator   (int) { iterator temp{ *this };   * this; return temp; }
        iterator operator  (const difference_type& n) const { iterator temp{ *this };  difference_type k{ n }; while (k--)  temp; return temp; }
        iterator& operator  =(const difference_type& n) { difference_type k{ n }; while (k--)  * this; return *this; };

        // Clumsy subtratcion
        iterator& operator --() { Node* tmp = head; while (tmp and tmp->next != this->iter) tmp = tmp->next; iter = tmp;  return *this; }

        // Comparison
        bool operator != (const iterator& other) const { return iter != other.iter; }
        bool operator == (const iterator& other) const { return iter == other.iter; }
        bool operator < (const iterator& other) const { return iter < other.iter; }
        bool operator > (const iterator& other) const { return iter > other.iter; }
        bool operator <= (const iterator& other) const { return iter <= other.iter; }
        bool operator >= (const iterator& other) const { return iter >= other.iter; }

        // Difference. Also complicated, because no random access
        difference_type operator-(const iterator& other) const {
            difference_type result{};
            Node* n{ iter };
            while (n and n != other.iter) {
                  result;
                n = n->next;
            }
            return result;
        }
    };

    // Begin and end function to initialize an iterator
    iterator begin() const { return iterator(head, head); }
    iterator end() const { return iterator(nullptr, head); }

    // Functions typcical for forward lists ----------------------------------------------------------------------
    // Easy, becuase we can operate form the current iterator and do not need the "previous" element
    iterator insertAfter(iterator& pos, const T& i) {
        iterator result(nullptr, head);
        if (pos.iter and pos.iter->next) {
            Node* n = new Node(i, pos.iter->next);
            pos.iter->next = n;
            result.iter = n;
        }
        return result;
    }
    iterator eraseAfter(iterator& pos) {
        iterator result(nullptr, head);
        if (pos.iter and pos.iter->next) {
            Node* tmp = pos.iter->next->next;
            delete pos.iter->next;
            pos.iter->next = tmp;
            result.iter = pos.iter->next;
        }
        return result;
    }
};
// Test/Driver Code
int main() {

    // Example for initilizer list
    SinglyLinkedList<int> sllbase{ 5,6,7,8,9,10,11,12,13,14,15 };
    // Show move constructor
    SinglyLinkedList<int> sll(std::move(sllbase));

    // Add some values in the front
    sll.push_front(4);
    sll.push_front(3);
    sll.push_front(2);
    sll.push_front(1);

    // Delete 1st element (Number 1)
    sll.pop_front();
    // Delete last element
    sll.pop_back();

    // Use a std::algorithm on our custom linked list. Works because we have an interator
    SinglyLinkedList<int>::iterator iter = std::find(sll.begin(), sll.end(), 8);
      iter;
    --iter;

    // Now add an element after 8
    iter = sll.insertAfter(iter, 88);
    // And delete the 9
    iter = sll.eraseAfter(iter);

    // Use range based for loop. Works because, we have iterators
    for (int i : sll)
        std::cout << i << ' ';


    // Reverse Output
    std::cout << "\n\n";
    std::reverse_iterator<SinglyLinkedList<int>::iterator> riter = std::make_reverse_iterator(sll.end());
    std::reverse_iterator<SinglyLinkedList<int>::iterator> riterEnd = std::make_reverse_iterator(sll.begin());

    for (; riter != riterEnd;   riter)
        std::cout << *riter << ' ';
    std::cout << "\n\n";

    return 0;
}

Then, you would add iterator functionality to the linked list class.

And last but not least, there is a wrapper class for reverse iterators. And with that, you can easily implement what you want. Please read here

But for that you need to create a --operator for your iterator. This is difficult for singly linked list and simple for double linked list.

C has already what you need. You can simply use it.

But, if we want to practice a little bit, then check some list implementations in the net and try to implement step by step.

And: Read book. This thing which consists of real paper :-)

CodePudding user response:

If you take the case of a Doubly Linked List, Its answer is Yes you can reverse this list by just making head pointer as the last node.

But, Note that now all the previous pointers are next pointers and all next pointers will be the previous pointers.

See below:

NULL<-1<-->2<-->3<-->4->NULL
      ↑
      Head

Now If you make head to 4, the list will be like this:

NULL<-1<-->2<-->3<-->4->NULL
                     ↑
                     Head

As seen the list is reversed but, the next pointers become previous and vice versa.

Coming To your Query that You have got a Garbage Value for printreverse function, I did the same code of yours but it works fine!

U can have a look at it below:

#include <bits/stdc  .h>
using namespace std;
struct Node
{
  struct Node *prev;
  int data;
  struct Node *next;
  Node(int x)
  {
    data = x;
    next = prev = nullptr;
  }
  Node(int x, Node*p,Node *q)
  {
    data = x;
    next = q;
    prev = p;
  }
};

void reverselist(Node * &head){
    Node *temp =head;
    Node *prevtemp = NULL;
    while(temp !=NULL){
        prevtemp = temp;
        temp = temp->next;
    }
    head = prevtemp;
}
void printlist(Node * &head){
    Node *temp = head;
    while(temp!=NULL){
        cout << temp->data << " ";
        temp = temp->next;
    }
}
void printreverse(Node * &head){
    Node *temp = head;
    while(temp!=NULL){
        cout << temp->data << " ";
        temp = temp->prev;
    }
}
int main()
{
  Node *node1 = new Node(0);
  Node *head = node1;
  Node *tail = node1;
  Node *temp=node1;
  int i=1;
  while(i<6){
    temp->next= new Node(i  ,temp,nullptr);
    temp=temp->next;
  }
  cout<<"List Before Reversal"<<endl;
  printlist(head);
  reverselist(head);
  cout<<endl<<"List After Reversal"<<endl;
  printreverse(head);
  return 0;
}

Output of above code is:

List Before Reversal 0 1 2 3 4 5
List After Reversal 5 4 3 2 1 0

  •  Tags:  
  • Related