Implementation - push_front(), pop_front()

push_front(data)

The push_front function adds a node to the front of the linked list. As with all insertion of nodes, the process can be generally described in the following manner:

  1. create a new node with the appropriate data

  2. link the new node up with the rest of the list

Let us start by considering how this would work in a general linked list. The steps are:

Step 1: Create new node, next node is the current front of list, there is no previous node which should be set to a nullptr

Step 2: Make the previous node of the current front of list point to the new node

Step 3: Make the front pointer point to the new node

Putting this together in code:

Node* nn = new Node(data,nullptr,front_);
front_->prev_=nn;
front_=nn;

Does above always work?

Now lets consider if the following would work if we started off with a linked list that is empty.

Step 1:

Step one will run without problems

Step 2:

front_ is nullptr, so front_->prev_ will crash. Thus, it looks like we need to skip this step or do something different if we have an empty list

Step 3:

The above will work. However, if we were to simply skip step 2, our linked list would not be valid as back_ would not be correctly set. The node we just added is not only the first node in the linked list but also the last. To make this work, we should add code to make back_ point to nn.

Putting all this together, The final function would look like the following:

void push_front(const T& data){
    Node* nn = new Node(data,nullptr,front_);
    if(front_){
        front_->prev_=nn;
    }
    else{
        back_=nn;
    }
    front_=nn;
}

pop_front()

The pop_front() function removes the first node from the linked list. The following are the general steps to remove a node:

  1. check to make sure that the list isn't empty (or that the node to be removed actually exist).

  2. unlink the node to be remove from the list (ensure other nodes are not lost in the process)

  3. deallocate the memory for the node

Given the above steps, let us consider how this would work in the general case. The steps for performing a pop_front are:

Step 1: Check to make sure list isn't empty. If it is do nothing. Otherwise continue to next steps

Step 2: Make a local pointer point to the first node in the list (hold this node so we don't lose it by accident)

Step 3: Make the front pointer point to the second node

Step 4: Make the new front node's previous pointer a nullptr

Step 5: deallocate the node we are removing

delete p; does not deallocate the pointer p itself. Rather it deallocates what the pointer points at. Thus, delete rm; deallocates the node rm points at.

The code snippet to do this is:

if(front_){
    Node* rm = front_;
    front_=front_->next_;
    front_->prev_=nullptr;
    delete rm;
}

A common misconception is that when you declare a pointer, you must use new. Unless you are trying to create an instance of the object, you do not need to use new. If you are using the pointer to simply refer to something that exist (like rm in the code above), there is no reason to use new and in fact, if you used new, you would end up with a memory leak.

Does the above always work?

The snippet of code above works for the general linked list and empty linked lists. However, does it always work? Let us consider the following situation. If we only had one node left in the list, would the above snippet still work?

If we perform the four steps inside the if statement from the above snippet, this is what we will see:

Step 2:

Above looks fine.

Step 3:

This also looks correct

Step 4:

If we were to try to do the above at this point we would end up crashing the program as front_ is currently nullptr. This step will either need to be skipped for lists with just one node or something differnt will need to be done

Step 5:

The final line is to delete the node, which won't crash. However, the list is not in a valid state. We have a back_ pointer that points to the memory location of the node that has now been deallocated. This pointer is therefore invalid. For lists with just one node, we must also adjust the back_ pointer to a nullptr as the list is now empty

Putting all this together our pop_front() function should look something like this:

void pop_front(){
    if(front_){
        Node* rm = front_;
        front_=front_->next_;
        if(front_==nullptr){  //if only one node exists
            back_=nullptr;
        }
        else{
            front_->prev_=nullptr;
        }
        delete rm;
    }
}

Last updated