Skip to main content

Doubly linked list in C++

In the previous post we have seen how to implement a singly linked list in C++. Let us implement doubly linked list today.

The difference between SLL and DLL is, DLL has two pointers in each node. One pointer points to next node and the other points to the previous node.

Also, we can have two external pointers - one pointer to first node of list - head and the other pointer pointing to last node of the list - tail.

So let us write structure node and class dlist.

struct node
{
    int val;
    struct node *next;
    struct node *prev;
};
class d_list
{
private:
  node * head;
  node *tail;
  
public:
   d_list(); 
   void append(int val); 
   void display();  
   void insert_beg(int n);
   node* find_node(int n); 
   bool delete_node(int n);
};

We have written a class with basic operations like append, display, find and delete etc. Two functions which are not needed when compared to linkedlist class are find_last() and find_previous(). Saves time.

Let us write constructor first. In the constructor we need to initialize two pointers - head and tail.

d_list::d_list()
{
   head = NULL;
   tail  = NULL;
}

Head and tail must be set to NULL. If we do not set, they have random values and we can not know whether the list is empty at all - empty list is recognized by head = tail = NULL.

Next let us write append function.

void d_list::append(int val)
{
    node *newnode = new node;
    newnode->val = val; 
    newnode->next = NULL;
    newnode->prev = NULL;

    if(head==NULL)
       head = newnode;
    else
    {
 tail->next = newnode;
        newnode->prev = tail;
    } 
    tail = newnode;
}

As usual we create a new node. Then we need to find last node and attach new node to last node. But that is not needed. Because we already have last node - tail.

We are linking tail to newnode. Also the newnode->prev link is set to tail. Then we set this new node as tail of the list.

What is the time complexity of append? Yes, it is O(1)!

One thing to take note here. If we add newnode to empty list(head == NULL), then the newnode will also be head of the list.

So that is the general idea. Any operation, we have to make sure we update both the links - prev and next.

Delete operation becomes easier in a doubly linked list. We need not traverse the list again for getting previous node.

Here is delete function. 


node *d_list::find_node(int num)
{
    node *temp = head;
    while(temp!=NULL && temp->val!=num)
 temp = temp->next;
    return temp;
}
 
bool d_list::delete_node(int num)
{
    node *delnd = find_node(num);
    if(delnd==NULL)
      return false;
    node *prevnode = delnd->prev;
    if(delnd==head)
    {
      /*head does not have previous node.we are deleting first node of list*/
      head = head->next;
      if(head==NULL)
 //list is empty
         head = tail = NULL;/****1****/
      else
  head->prev = NULL;       
      delete delnd;
    }else  
    { 
       prevnode->next = delnd->next;
       if(delnd==tail) 
    tail = prevnode;
       else
           delnd->next->prev=prevnode;
       delete delnd;
     }
    return true;
}

We are also taking care of updating pointers if we are deleting the head.

Also observe comment marked as 1. Here we are deleting first node and list had only one node. We delete it and list becomes empty, which means both head and tail must be set to NULL.

Here is the complete class and test program.

#include<iostream>
using namespace std;
struct node
{
    int val;
    struct node *next;
    struct node *prev;
};
class d_list
{
private:
  node * head;
  node *tail;
  
public:
   d_list(); 
   void append(int val); 
   void display();  
   void insert_beg(int n);
   node* find_node(int n); 
   bool delete_node(int n);
};
d_list::d_list()
{
   head = NULL;
   tail  = NULL;
} 
void d_list::append(int val)
{
    node *newnode = new node;
    newnode->val = val; 
    newnode->next = NULL;
    newnode->prev = NULL;

    if(head==NULL)
       head = newnode;
    else
    {
 node*last = tail;
 tail->next = newnode;
        newnode->prev = tail;
    } 
    tail = newnode;
}
node *d_list::find_node(int num)
{
    node *temp = head;
    while(temp!=NULL && temp->val!=num)
 temp = temp->next;
    return temp;
}
 
bool d_list::delete_node(int num)
{
    node *delnd = find_node(num);
    if(delnd==NULL)
      return false;
    node *prevnode = delnd->prev;
    if(delnd==head)
    {
      /*head does not have previous node.we are deleting first node of list*/
      head = head->next;
      if(head==NULL)
 //list is empty
         head = tail = NULL;
      else
  head->prev = NULL;       
      delete delnd;
    }else  
    { 
       prevnode->next = delnd->next;
       if(delnd==tail) 
    tail = prevnode;
       else
           delnd->next->prev=prevnode;
       delete delnd;
     }
    return true;
}

void d_list::insert_beg(int val)
{
    node *newnode = new node;
    newnode->val = val;
    newnode->next = NULL;
    newnode->prev = NULL;
   
    newnode->next = head;
    if(head)
 head->prev = newnode;
    else
 tail = newnode;
    head = newnode;    
}
void d_list::display( )
{
    if(head==NULL)
    {
 cout<<"list is empty\n";
        return;
    }
    cout<<"Your list is ";
    for(node*temp=head;temp!=NULL;temp = temp->next)
    {
 cout<<temp->val<<"  "; 
    }

    cout<<"Your reverse list is ";
    for(node*temp=tail;temp!=NULL;temp = temp->prev)
    {
 cout<<temp->val<<"  "; 
    }
    cout<<endl;
} 

int main()
{
   d_list l1;
   while(true)
   {
 int n;
        cout<<"Enter value (-1 to stop)";
        cin>>n;
 if(n==-1)
    break;
 l1.insert_beg(n);
   }
   l1.display();
   int n;
   cout<<"Enter a value to be added to the end of the list";
   cin>>n;
   l1.append(n);
   l1.display();
   while(true)
   {
 int n;
        cout<<"Enter value to be deleted (-1 to stop)";
        cin>>n;
        if(n==-1)
    break;
 if(l1.delete_node(n))
           cout<<"Node deleted successfully";
         else
           cout<<"Could not delete the node";
         l1.display();
    }       
}

You can download the above program from here.

Comments

Popular posts from this blog

In order traversal of nodes in the range x to y

Question : Write a function for in-order traversal of nodes in the range x to y from a binary search tree. This is quite a simple function. As a first solution we can just traverse our binary search tree in inorder and display only the nodes which are in the range x to y. But if the current node has a value less than x, do we have to traverse its left subtree? No. Because all the nodes in left subtree will be smaller than x. Similarly if the current node has a key value more than y, we need not visit its right subtree. Now we are ready to write our algorithm.     if nd is NOT NULL  if nd->val >=x then visit all the nodes of left subtree of nd recursively display nd->val if nd->val <y then visit all the nodes of right subtree of nd recursively  That's all. We have our function ready. void in_order_middle (NODEPTR nd, int x, int y) { if (nd) { if (nd -> val >= x) in_order_middle(nd...

Josephus problem

Question: Write a function to delete every k th node from circular linked list until only one node is left. This has a story associated with it. Flavius Josephus was Jewish Historian from 1st century. He and 40 other soldiers were trapped in a cave by Romans. They decided to kill themselves rather than surrendering to Romans. Their method was like this. All the soldiers will stand in a circle and every k th soldier will be shot dead. Josephus said to have calculated the starting point so that he would remain alive. So we have similar problem at hand. We delete every kth node in a circular list. Eventually only one node will be left. e.g. Let us say this is our list And we are deleting every third node.  We will delete 30. Then we delete 60. Next we delete 10. Next it will be 50. Next to be deleted is 20. Next 80. This continues. Implementation   We can count k-1 nodes and delete next node. This can be repeated in  a loop. What must be the termina...

Lowest common ancestor of binary search tree

Question : Write a function to print the lowest common ancestor of two nodes in a binary search tree.  Lowest common ancestor of two nodes x and y in a binary tree is the lowest node that has both x and y as descendants. Here lowest common ancestor of 1 and 7 is 3. LCA of 13 and 7 is root - 8. And LCA of 6 and 7 is 6 itself. The program to find gets complicated for an ordinary binary tree. But for a binary search tree, it is quite simple. As we see from the diagram above, the paths to 1 and 4 are common till the node 3. And at 3 they branch in different directions. So 3 is our LCA. That is lowest common ancestor is the node where the paths to x and y from root deviate. As long as they branch in same direction, we continue to traverse. When they branch in different directions, that is the lowest common ancestor. So let us write a simple algorithm, set temp=root if temp->val >x and temp->val>y temp = temp->left else if temp->val<x and ...