Removing an item at the end of the linked list

The effect of removeLast() --- removing an item at the end of a linked list:

  • Example: the linked list before the removal of the last node:

  • The linked list after the removal of the last node:

  • Remove at end algorithm:

       (1) Find the predecesor node of the last node
       (2) Unlink the last node from its predecesor node
       (3) De-allocate the deleted node
       (4) Return the new first node of the list
    

Classic algorithm to find the last node

  • The list traversal algorithm for finding the last node:

    The algorithm to find the last node is as follows:

    
       struct Node *current = first;
    
       while ( current->next != NULL ) // Find last list element
       {
    
           current = current->next;
       }
    
    
       // current points to the last node
    

Algorithm to find the predecessor of the last node

  • The list traversal algorithm for finding the predecessor node of the last node:

    The algorithm to find the predecessor of the last node is as follows:

       struct Node *previous = first;
       struct Node *current = first;
    
       while ( current->next != NULL ) // Find last list element
       {
           previous = current; // Keep track of the previous node
           current = current->next;
       }
    
       // previous points to the predecessor node of last node
       // current points to the last node
    

Implementation of removeLast( )

 

Let's implement the removeLast( ) method:

   // Delete the node at the end of this list
   struct Node *removeLast( )
   {

       // General case  (take care of edge cases later)
       struct Node *current = h;
       struct Node *previous = h;

       while( current->next != NULL )  // Find the last node
       {
           previous = current;
           current = current->next;    // Advance to next node
       }

       previous->next = NULL;   // Unlink the last node (from its predecessor)
       free(current);           // De-allocate deleted node

       return h;                // Return (confirm first node was unchanged)
   }

Implementation of removeLast( )

 

Start the search at the first node:

   // Delete the node at the end of this list
   struct Node *removeLast( )
   {

       // General case  (take care of edge cases later)
       struct Node *current  = h;
       struct Node *previous = h;

       while( current->next != NULL )  // Find the last node
       {
           previous = current;
           current = current->next;    // Advance to next node
       }

       previous->next = NULL;   // Unlink the last node (from its predecessor)
       free(current);           // De-allocate deleted node

       return h;                // Return (confirm first node was unchanged)
   }

Implementation of removeLast( )

 

Find the last node while remembering its previous node:

   // Delete the node at the end of this list
   struct Node *removeLast( )
   {

       // General case  (take care of edge cases later)
       struct Node *current  = h;
       struct Node *previous = h;

       while( current->next != NULL )  // Find the last node
       {
           previous = current;
           current  = current->next;    // Advance to next node
       }

       previous->next = NULL;   // Unlink the last node (from its predecessor)
       free(current);           // De-allocate deleted node

       return h;                // Return (confirm first node was unchanged)
   }

Implementation of removeLast( )

 

Unlink the last node from the previous node:

   // Delete the node at the end of this list
   struct Node *removeLast( )
   {

       // General case  (take care of edge cases later)
       struct Node *current  = h;
       struct Node *previous = h;

       while( current->next != NULL )  // Find the last node
       {
           previous = current;
           current  = current->next;    // Advance to next node
       }

       previous->next = NULL;   // Unlink the last node (from its predecessor)
       free(current);           // De-allocate deleted node

       return h;                // Return (confirm first node was unchanged)
   }

Implementation of removeLast( )

 

De-allocate the deleted node:

   // Delete the node at the end of this list
   struct Node *removeLast( )
   {

       // General case  (take care of edge cases later)
       struct Node *current  = h;
       struct Node *previous = h;

       while( current->next != NULL )  // Find the last node
       {
           previous = current;
           current  = current->next;    // Advance to next node
       }

       previous->next = NULL;   // Unlink the last node (from its predecessor)
       free(current);           // De-allocate deleted node

       return h;                // Return (confirm first node was unchanged)
   }

Implementation of removeLast( )

 

Return the reference of the new first node: (in this case, we re-affirm h);

   // Delete the node at the end of this list
   struct Node *removeLast( )
   {

       // General case  (take care of edge cases later)
       struct Node *current  = h;
       struct Node *previous = h;

       while( current->next != NULL )  // Find the last node
       {
           previous = current;
           current  = current->next;    // Advance to next node
       }

       previous->next = NULL;   // Unlink the last node (from its predecessor)
       free(current);           // De-allocate deleted node

       return h;                // Return (confirm first node was unchanged)
   }

Implementation of removeLast( )

 

Check for edge cases.... ( Note:: the general case returns a value h NULL)

   // Delete the node at the end of this list
   struct Node *removeLast( )
   {
      // Detect and handle edge cases

      if ( h == NULL )
         return NULL;      // Return NULL

      if ( h->next == NULL )
      {
         free(h);          // De-allocated deleted node
         return NULL;      // Return NULL
      }

      // General case  
          The general case returns h != NULL
   }

Implementation of removeLast( )

 

Edge case 1: empty list....   the resulting list of removeLast() is the empty list

   // Delete the node at the end of this list
   struct Node *removeLast( )
   {
      // Detect and handle edge cases

      if ( h == NULL )     // Edge case 1: the empty list
         return NULL;      // Return the empty list

      if ( h->next == NULL )
      {
         free(h);          // De-allocated deleted node
         return NULL;      // Return NULL
      }

      // General case  
          The general case returns h != NULL
   }

Implementation of removeLast( )

 

Edge case 2: list with a single element ---> removal results in an empty list

   // Delete the node at the end of this list
   struct Node *removeLast( )
   {
      // Detect and handle edge cases

      if ( h == NULL )     // Edge case 1: the empty list
         return NULL;      // Return the empty list

      if ( h->next == NULL )// Edge case 2: the "nearly" empty list
      {
         free(h);          // De-allocated deleted node
         return NULL;      // Return the empty list
      }

      // General case  
          The general case returns h != NULL
   }

DEMO: demo/C/Linked-list/removeLast.c

Study advise I gave in CS171...

  • Important note:

    • There are many kinds of linked lists
      (We have only been working with one kind !)

    • There are many different operations on a linked list

      • It's futile to memorize the different algorithms

  • There is a common principle in all the algorithms:

    • Know/understand what result you want/need to achieve

    • Know/understand how to to achieve the result

    • How to to achieve the result that you want/need:

      • Traverse the linked list to the proper node that contain the link that needs to be updated

      • Modify the link and make it point to the correct node