Removing a specific item from the linked list

The effect of remove(key) --- removing the (first) item from a linked list that contains a item==key:

  • Example: the linked list before the removal of the node containing "or":

  • The linked list after the removal of the node containing "or":

  • Remove node containg item==key algorithm:

       (1) Find the predecesor node of the node containing item==key
       (2) Unlink the targeted node from its predecesor node
    

Note: the technique for step (1) is similar to that in the removeLast() algorithm !!!

Recall: the standard list traversal algorithm

  • Review: the list traversal algorithm:

    The algorithm to visit all nodes is as follows:

    
       Node<T> current = first;
    
       // Visit all node
       while ( current != null )
       {
    
           current = current.next;  // Move to next node when not last node
       }
    
    
       // current == null here...
    
    

Algorithm to find the (first) node that contains item == key

  • The algorithm that finds the (first) node item is equal to a given key: (key="or")

    We stop when current.item is equal to the key:

    
       Node<T> current = first;
    
       // Find the (first) node that contains key
       while ( current != null  ) 
       {
           if ( current.item equal key ) break;
           current = current.next;  // Move to next node when not found
       }
    
    
       // current points to the node containing key 
       // or current == null: not found
    

Algorithm to find the (first) node that contains item == key

  • The algorithm that finds the (first) node item is equal to a given key: (key="or")

    The algorithm is usually written as:

    
       Node<T> current = first;
    
       // Find the (first) node that contains key
       while ( current != null && current.item not equal key ) 
       {
         
           current = current.next;  // Move to next node when not found
       }
    
    
       // current points to the node containing key 
       // or current == null: not found
    

Algorithm to find the predecessor node of (first) node that contains item == key

  • The algorithm that finds the predecessor node of the node a given that contains key

    We find the node containing the given key while keeping track of its previous node:

       Node<T> previous = first;
       Node<T> current = first;
    
       // Find the (first) node that contains key
       while ( current != null && current.item not equal key ) 
       {
           previous = current; // Keep track of the previous node
           current = current.next;
       }
    
       // previous points to the predecessor node of current
       // current points to the node containing the given key 
       // or current == null: not found
    

Implementation of remove(key)

Let's implement the remove(key) method:

   // Delete the node that contains key
   public void remove(T key)
   {     // General case  (take care of edge cases later)
         // Find the previous node of the node that contains key
         Node<T> current  = first;
         Node<T> previous = first;
         (1) Find the predecesor node of the node containing item==key 
         while( current!=null && !current.item.equals(key) )
         {
            previous = current;
            current = current.next;
         }

         if( current == null )
         { // key not found
            throw new NoSuchElementException();
         }

         (2) Unlink the targeted node from its predecesor node
   }

Implementation of remove(key)

(1) Find the predecesor node of the node containing item==key:

   // Delete the node that contains key
   public void remove(T key)
   {     // General case  (take care of edge cases later)
         // Find the previous node of the node that contains key
         Node<T> current  = first;   // Initialize
         Node<T> previous = first;

         while( current != null && !current.item.equals(key) )
         {
            previous = current;        // Keep track of the previous node
            current  = current.next;   // Moev to next node
         }

         // previous points to the predecessor node of current
         // current points to the node containing the given key 
         // or current == null: not found


         (2) Unlink the targeted node from its predecesor node
   }

Implementation of remove(key)

(2a) If key is not found, we report an error (throw an exception):

   // Delete the node that contains key
   public void remove(T key)
   {     // General case  (take care of edge cases later)
         // Find the previous node of the node that contains key
         Node<T> current  = first;   // Initialize
         Node<T> previous = first;

         while( current != null && !current.item.equals(key) )
         {
            previous = current;        // Keep track of the previous node
            current  = current.next;   // Moev to next node
         }

         if( current == null )
         {  key not found
            throw new NoSuchElementException();
         }

         (2) Unlink the targeted node from its predecesor node
   }

Implementation of remove(key)

(2b) If the key was found in the linked list, we unlink the node from its predecessor node:

   // Delete the node that contains key
   public void remove(T key)
   {     // General case  (take care of edge cases later)
         // Find the previous node of the node that contains key
         Node<T> current  = first;   // Initialize
         Node<T> previous = first;

         while( current != null && !current.item.equals(key) )
         {
            previous = current;        // Keep track of the previous node
            current  = current.next;   // Moev to next node
         }

         if( current == null )
         {  key not found
            throw new NoSuchElementException();
         }

         previous.next = current.next; // Unlink the current node from list
   }

Handling the edge cases in remove(key)

There are 2 edge cases: (1) the empty list and (2) the first node contains the key (updates first)

   // Delete the node that contains key
   public void remove(T key)
   {
      if ( edge case 1 )
      {
         ...
      }
      else if ( edge case 2 )
      {
         ... must update first
      }
      else
      {
         // General case updates previous.next
         ...
         Omitted for brevity

         previous.next = current.next; // Unlink the current node from list
       }
   }

Detect and handle the edge case 1 in remove(key)

Detect and handle edge case 1: the empty list

   // Delete the node that contains key
   public void remove(T key)
   {
      if ( first == null )
      {
         throw new NoSuchElementException();
      }
      else if ( edge case 2 )
      {
         ... must update first
      }
      else
      {
         // General case updates previous.next
         ...
         Omitted for brevity

         previous.next = current.next; // Unlink the current node from list
       }
   }

Detecting the edge case 2 in remove(key)

Detecting the edge case 2: the first node contains the given key

   // Delete the node that contains key
   public void remove(T key)
   {
      if ( first == null )
      {
         throw new NoSuchElementException();
      }
      else if ( first.item.equals(key) )
      {
         ... must update first
      }
      else
      {
         // General case updates previous.next
         ...
         Omitted for brevity

         previous.next = current.next; // Unlink the current node from list
       }
   }

Handling the edge case 2 in remove(key)

Handling the edge case 2: make the 2nd node into the first node of the list

   // Delete the node that contains key
   public void remove(T key)
   {
      if ( first == null )
      {
         throw new NoSuchElementException();
      }
      else if ( first.item.equals(key) )
      {
         first = first.next;  // The 2nd node is now the new 1st node
      }
      else
      {
         // General case updates previous.next
         ...
         Omitted for brevity

         previous.next = current.next; // Unlink the current node from list
       }
   }

Demo program showing the operation of remove(key)

   public static void main(String[] args)
   {
      System.out.println("Demo getFirst(), getLast(), get(pos)\n");

      GenericLinkedList<Integer> intList = new GenericLinkedList<Integer>();

      intList.addFirst(89);
      intList.addFirst(78);
      intList.addFirst(34);
      intList.addFirst(23);
      intList.addFirst(12);
      System.out.println(intList); // 12 -> 23 -> 34 -> 78 -> 89

      intList.remove(34);
      System.out.println("After remove(34):");
      System.out.println(intList);

      intList.remove(89);
      System.out.println("After remove(89):");
      System.out.println(intList);

      intList.remove(12);
      System.out.println("After remove(12):");
      System.out.println(intList);

      System.out.println("remove(99): (not in list)");
      intList.remove(12);
   }

DEMO: demo/11-linked-list/05-remove-item/Demo.java + GenericLinkedList.java