Qingquan-Li / blog

My Blog
https://Qingquan-Li.github.io/blog/
132 stars 16 forks source link

Linked List as an ADT, Unordered Linked List, Ordered Linked List #243

Open Qingquan-Li opened 1 year ago

Qingquan-Li commented 1 year ago

Reference: Book: Data Structures Using C++ (Second Edition, D.S. Malik)

Table of Contents:


1. Linked List as an ADT

Rather than discuss specific lists such as a list of integers or a list of strings, this section discusses linked lists as an abstract data type (ADT). Using templates, this section gives a generic definition of linked lists.

The basic operations on linked lists are as follows:

  1. Initialize the list.
  2. Determine whether the list is empty.
  3. Print the list.
  4. Find the length of the list.
  5. Destroy the list.
  6. Retrieve the info contained in the first node.
  7. Retrieve the info contained in the last node.
  8. Search the list for a given item.
  9. Insert an item in the list.
  10. Delete an item from the list.
  11. Make a copy of the linked list.

We will define the class linkedListType to implement the basic operations on a linked list as an abstract class. Using the principal of inheritance, we derive two classes— unorderedLinkedList and orderedLinkedList—from the class linkedListType.

1.1 Structure of linked list nodes

//Definition of the node
template <class Type>
struct nodeType
{
    Type info;
    nodeType<Type> *link;
};

1.2 Member variables of the class linkedListType

protected:
    int count; //variable to store the number of elements in the list
    nodeType<Type> *first; //pointer to the first node of the list
    nodeType<Type> *last;  //pointer to the last node of the list

1.3 Linked List Iterators (class linkedListIterator)

One of the basic operations performed on a list is to process each node of the list. This requires the list to be traversed starting at the first node. Moreover, a specific application requires each node to be processed in a very specific way. A common technique to accomplish this is to provide an iterator.

An iterator is an object that produces each element of a container, such as a linked list, one element at a time.

The two most common operations on iterators are ++ (the increment operator) and * (the dereferencing operator). The increment operator advances the iterator to the next node in the list while the dereferencing operator returns the info of the current node.

Note that an iterator is an object. So we need to define a class, which we will call linkedListIterator, to create iterators to objects of the class linkedListType. The iterator class would have one member variable pointing to (the current) node.

1.3.1 linkedListIterator class declaration

// This class specifies the members to implement an iterator to a linked list.

template <class Type>
class linkedListIterator
{
public:
    linkedListIterator();
      //Default constructor
      //Postcondition: current = NULL;

    linkedListIterator(nodeType<Type> *ptr);
      //Constructor with a parameter.
      //Postcondition: current = ptr;

    Type operator*();
      //Function to overload the dereferencing operator *.
      //Postcondition: Returns the info contained in the node.

    linkedListIterator<Type> operator++();
      //Overload the preincrement operator.
      //Postcondition: The iterator is advanced to the next node.

    bool operator==(const linkedListIterator<Type>& right) const;
      //Overload the equality operator.
      //Postcondition: Returns true if this iterator is equal to
      //    the iterator specified by right, otherwise it returns
      //    false.

    bool operator!=(const linkedListIterator<Type>& right) const;
      //Overload the not equal to operator.
      //Postcondition: Returns true if this iterator is not equal to
      //    the iterator specified by right, otherwise it returns
      //    false.

private:
    nodeType<Type> *current; //pointer to point to the current
                             //node in the linked list
};

1.3.2 The UML class diagram of the class linkedListIterator

UML class diagram of the class linkedListIterator

1.3.3 The definitions of the functions of the class linkedListIterator

template <class Type>
linkedListIterator<Type>::linkedListIterator()
{
    current = NULL;
}

template <class Type>
linkedListIterator<Type>::linkedListIterator(nodeType<Type> *ptr)
{
    current = ptr;
}

template <class Type>
Type linkedListIterator<Type>::operator*()
{
    return current->info;
}

template <class Type>
linkedListIterator<Type> linkedListIterator<Type>::operator++()
{
    current = current->link;
    return *this;
}

template <class Type>
bool linkedListIterator<Type>::operator==
                (const linkedListIterator<Type>& right) const
{
    return (current == right.current);
}

template <class Type>
bool linkedListIterator<Type>::operator!=
                (const linkedListIterator<Type>& right) const
{
    return (current != right.current);
}

1.4 Abstract Class linkedListType

Now that we have defined the classes to implement the node of a linked list and an iterator to a linked list, next, we describe the class linkedListType to implement the basic porperties of a linked list.

1.4.1 linkedListType class declaration

The following abstract class defines the basic properties of a linked list as an ADT:

// This class specifies the members to implement the basic
// properties of a linked list. This is an abstract class.
// We cannot instantiate an object of this class.

template <class Type>
class linkedListType
{
public:
    const linkedListType<Type>& operator=(const linkedListType<Type>&);
      //Overload the assignment operator.

    void initializeList();
      //Initialize the list to an empty state.
      //Postcondition: first = NULL, last = NULL, count = 0;

    bool isEmptyList() const;
      //Function to determine whether the list is empty.
      //Postcondition: Returns true if the list is empty, otherwise
      //    it returns false.

    void print() const;
      //Function to output the data contained in each node.
      //Postcondition: none

    int length() const;
      //Function to return the number of nodes in the list.
      //Postcondition: The value of count is returned.

    void destroyList();
      //Function to delete all the nodes from the list.
      //Postcondition: first = NULL, last = NULL, count = 0;

    Type front() const;
      //Function to return the first element of the list.
      //Precondition: The list must exist and must not be empty.
      //Postcondition: If the list is empty, the program terminates;
      // otherwise, the first element of the list is returned.

    Type back() const;
      //Function to return the last element of the list.
      //Precondition: The list must exist and must not be empty.
      //Postcondition: If the list is empty, the program
      //    terminates; otherwise, the last
      //    element of the list is returned.

    virtual bool search(const Type& searchItem) const = 0;
      //Function to determine whether searchItem is in the list.
      //Postcondition: Returns true if searchItem is in the list,
      //    otherwise the value false is returned.

    virtual void insertFirst(const Type& newItem) = 0;
      //Function to insert newItem at the beginning of the list.
      //Postcondition: first points to the new list, newItem is
      //    inserted at the beginning of the list, last points to
      //    the last node in the list, and count is incremented by 1.

    virtual void insertLast(const Type& newItem) = 0;
      //Function to insert newItem at the end of the list.
      //Postcondition: first points to the new list, newItem is
      //    inserted at the end of the list, last points to the
      //    last node in the list, and count is incremented by 1.

    virtual void deleteNode(const Type& deleteItem) = 0;
      //Function to delete deleteItem from the list.
      //Postcondition: If found, the node containing deleteItem is
      //    deleted from the list. first points to the first node,
      //    last points to the last node of the updated list, and
      //    count is decremented by 1.

    linkedListIterator<Type> begin();
      //Function to return an iterator at the beginning of the
      //linked list.
      //Postcondition: Returns an iterator such that current is set
      //    to first.

    linkedListIterator<Type> end();
      //Function to return an iterator one element past the
      //last element of the linked list.
      //Postcondition: Returns an iterator such that current is set
      //    to NULL.

    linkedListType();
      //default constructor
      //Initializes the list to an empty state.
      //Postcondition: first = NULL, last = NULL, count = 0;

    linkedListType(const linkedListType<Type>& otherList);
      //copy constructor

    ~linkedListType();
      //destructor
      //Deletes all the nodes from the list.
      //Postcondition: The list object is destroyed.

protected:
    int count; //variable to store the number of list elements
    nodeType<Type> *first; //pointer to the first node of the list
    nodeType<Type> *last; //pointer to the last node of the list

private:
    void copyList(const linkedListType<Type>& otherList);
      //Function to make a copy of otherList.
      //Postcondition: A copy of otherList is created and assigned
      //    to this list.
};

1.4.2 The UML class diagram of the class linkedListType

UML class diagram of the class linkedListType

The instance variables first and last, as defined earlier, of the class linkedListType are protected, not private, because we will derive the classes unorderedLinkedList and orderedLinkedList from the class linkedListType.

Because each of the classes unorderedLinkedList and orderedLinkedList will provide separate definitions of the functions search, insertFirst, insertLast, and deleteNode, and because these functions would access the instance variable, to provide direct access to the instance variables, the instance variables are declared as protected.

The definition of the class linkedListType includes a member function to overload the assignment operator. For classes that include pointer data members, the assignment operator must be explicitly overloaded. For the same reason, the definition of the class also includes a copy constructor.

Notice that the definition of the class linkedListType contains the member function copyList, which is declared as a private member. This is because this function is used only to implement the copy constructor and overload the assignment operator.

1.4.3 The definitions of the functions of the class linkedListType

We write the definitions of the nonabstract functions of the class LinkedListClass linkedListType.

isEmptyList

The list is empty if first is NULL. Therefore, the definition of the function isEmptyList to implement this operation is as follows:

template <class Type>
bool linkedListType<Type>::isEmptyList() const {
    return (first == NULL);
}

Default Constructor

It simply initializes the list to an empty state. Recall that when an object of the linkedListType type is declared and no value is passed, the default constructor is executed automatically.

template <class Type>
linkedListType<Type>::linkedListType() //default constructor
{
    first = NULL;
    last = NULL;
    count = 0;
}

Destroy the List

The function destroyList deallocates the memory occupied by each node.

template <class Type>
void linkedListType<Type>::destroyList()
{
    nodeType<Type> *temp;  //pointer to deallocate the memory
                           //occupied by the node
    while (first != NULL)  //while there are nodes in the lis
    {
        temp = first;        //Set temp to the current node
        first = first->link; //advance first to the next node
        delete temp; //deallocate the memory occupied by temp
    }
    last = NULL; //initialize last to NULL; first has already
                 //been set to NULL by the while loop
    count = 0;
}

Initialize the List

The default constructor or the copy constructor has already initialized the list when the list object was declared. This operation, in fact, reinitializes the list to an empty state, and so it must delete the nodes (if any) from the list.

template <class Type>
void linkedListType<Type>::initializeList()
{
    destroyList(); //if the list has any nodes, delete them
}

Print the List

The member function print prints the data contained in each node. To print the data contained in each node, we must traverse the list starting at the first node. Because the pointer first always points to the first node in the list, we need another pointer to traverse the list. (If we use first to traverse the list, the entire list will be lost.)

template <class Type>
void linkedListType<Type>::print() const
{
    nodeType<Type> *current; //pointer to traverse the list

    current = first; //set current point to the first node
    while (current != NULL) //while more data to print
    {
        cout << current->info << " ";
        current = current->link;
    }
}

Length of a List

The length of a linked list (that is, how many nodes are in the list) is stored in the variable count. Therefore, this function returns the value of this variable.

template <class Type>
int linkedListType<Type>::length() const
{
    return count;
}

Retrieve the Data of the First Node

Notice that if the list is empty, the assert statement terminates the program. Therefore, before calling this function check, you have to check to see whether the list is nonempty.

template <class Type>
Type linkedListType<Type>::front() const
{
    assert(first != NULL);
    return first->info; //return the info of the first node
}

Retrieve the Data of the Last Node

Notice that if the list is empty, the assert statement terminates the program. Therefore, before calling this function, you have to check to see whether the list is nonempty.

template <class Type>
Type linkedListType<Type>::back() const
{
    assert(last != NULL);
    return last->info; //return the info of the last node
}

Begin and End

The function begin returns an iterator to the first node in the linked list, and the function end returns an iterator to the last node in the linked list.

template <class Type>
linkedListIterator<Type> linkedListType<Type>::begin()
{
    linkedListIterator<Type> temp(first);
    return temp;
}

template <class Type>
linkedListIterator<Type> linkedListType<Type>::end()
{
    linkedListIterator<Type> temp(NULL);
    return temp;
}

Copy the List

The function copyList makes an identical copy of a linked list. Therefore, we traverse the list to be copied starting at the first node. Corresponding to each node in the original list, we do the following:

  1. Create a node and call it newNode.
  2. Copy the info of the node (in the original list) into newNode.
  3. Insert newNode at the end of the list being created.
template <class Type>
void linkedListType<Type>::copyList
                (const linkedListType<Type>& otherList)
{
    nodeType<Type> *newNode; //pointer to create a node
    nodeType<Type> *current; //pointer to traverse the

    if (first != NULL) //if the list is nonempty, make it empty
        destroyList();

    if (otherList.first == NULL) //otherList is empty
    {
        first = NULL;
        last = NULL;
        count = 0;
    }
    else
    {
        current = otherList.first; //current points to the 
                                   //list to be copied
        count = otherList.count;

        //copy the first node:
        first = new nodeType<Type>;  //create the node
        first->info = current->info; //copy the info
        first->link = NULL; //set the link field of the node to NULL
        last = first; //make last point to the first node
        current = current->link; //make current point to the next node

        //copy the remaining list:
        while (current != NULL)
        {
            newNode = new nodeType<Type>;  //create a node
            newNode->info = current->info; //copy the info
            newNode->link = NULL; //set the link of newNode to NULL

            last->link = newNode; //attach newNode after last
            last = newNode; //make last point to the actual last node
            current = current->link; //make current point to the next node
        }//end while
    }//end else
}//end copyList

Destructor

The destructor deallocates the memory occupied by the nodes of a list when the class object goes out of scope. Because memory is allocated dynamically, resetting the pointers first and last does not deallocate the memory occupied by the nodes in the list. We must traverse the list, starting at the first node, and delete each node in the list. The list can be destroyed by calling the function destroyList.

template <class Type>
linkedListType<Type>::~linkedListType() //destructor
{
    destroyList();
}

Copy Constructor

Because the class linkedListType contains pointer data members, the definition of this class contains the copy constructor. Recall that, if a formal parameter is a value parameter, the copy constructor provides the formal parameter with its own copy of the data. The copy constructor also executes when an object is declared and initialized using another object.

The copy constructor makes an identical copy of the linked list. This can be done by calling the function copyList. Because the function copyList checks whether the original is empty by checking the value of first, we must first initialize the pointer first to NULL before calling the function copyList.

template <class Type>
linkedListType<Type>::linkedListType (const linkedListType<Type>& otherList)
{
    first = NULL;
    copyList(otherList);
}

Overloading the Assignment Operator

The definition of the function to overload the assignment operator for the class linkedListType is similar to the definition of the copy constructor. We give its definition for the sake of completeness.

//overload the assignment operator
template <class Type>
const linkedListType<Type>& linkedListType<Type>::operator=
                    (const linkedListType<Type>& otherList)
{
    if (this != &otherList) //avoid self-copy
    {
        copyList(otherList);
    }

    return *this;
}

2. Unordered Linked Lists

We derive the class unorderedLinkedList from the abstract class linkedListType and implement the operations search, insertFirst, insertLast, and deleteNode.

2.1 unorderedLinkedList class declaration

The following class defines an unordered linked list as an ADT:

// This class specifies the members to implement the basic
// properties of an unordered linked list. This class is
// derived from the class linkedListType.

template <class Type>
class unorderedLinkedList: public linkedListType<Type>
{
public:
    bool search(const Type& searchItem) const;
      //Function to determine whether searchItem is in the list.
      //Postcondition: Returns true if searchItem is in the list,
      // otherwise the value false is returned.

    void insertFirst(const Type& newItem);
      //Function to insert newItem at the beginning of the list.
      //Postcondition: first points to the new list, newItem is
      //    inserted at the beginning of the list, last points to
      //    the last node, and count is incremented by 1.

    void insertLast(const Type& newItem);
      //Function to insert newItem at the end of the list.
      //Postcondition: first points to the new list, newItem is
      //    inserted at the end of the list, last points to the
      //    last node, and count is incremented by 1.

    void deleteNode(const Type& deleteItem);
      //Function to delete deleteItem from the list.
      //Postcondition: If found, the node containing deleteItem
      //    is deleted from the list. first points to the first
      //    node, last points to the last node of the updated
      //    list, and count is decremented by 1.
};

2.2 The UML class diagram of the class unorderedLinkedList and the inheritance hierarchy

UML class diagram of the class unorderedLinkedList

2.3 The definitions of the member functions of the class unorderedLinkedList

2.3.1 Search the List

The member function search searches the list for a given item. If the item is found, it returns true; otherwise, it returns false. Because a linked list is not a random access data structure, we must sequentially search the list starting from the first node.

template <class Type>
bool unorderedLinkedList<Type>::search(const Type& searchItem) const
{
    nodeType<Type> *current; //pointer to traverse the list
    bool found = false;

    current = first; //set current to point to the first
                     //node in the list

    while (current != NULL && !found) //search the list
    {
        if (current->info == searchItem) //searchItem is found
            found = true;
        else
            current = current->link; //make current point to
                                     //the next node
        return found;
    }
}

2.3.2 Insert the First Node

The function insertFirst inserts the new item at the beginning of the list—that is, before the node pointed to by first. The steps needed to implement this function are as follows:

  1. Create a new node.
  2. If unable to create the node, terminate the program.
  3. Store the new item in the new node.
  4. Insert the node before first.
  5. Increment count by 1.
template <class Type>
void unorderedLinkedList<Type>::insertFirst(const Type& newItem)
{
    nodeType<Type> *newNode; //pointer to create the new node

    newNode = new nodeType<Type>; //create the new node
    newNode->info = newItem; //store the new item in the node
    newNode->link = first; //insert newNode before first
    first = newNode; //make first point to the actual first node
    count++; //increment count

    if (last == NULL) //if the list was empty, newNode is also
                      //the last node in the list
        last = newNode;
}

2.3.3 Insert the Last Node

template <class Type>
void unorderedLinkedList<Type>::insertLast(const Type& newItem)
{
    nodeType<Type> *newNode; //pointer to create the new node

    newNode = new nodeType<Type>; //create the new node
    newNode->info = newItem; //store the new item in the node
    newNode->link = NULL; //set the link field of newNode to NULL

    if (first == NULL) //if the list is empty, newNode is
                       //both the first and last node
    {
        first = newNode;
        last = newNode;
        count++; //increment count
    }
    else //the list is not empty, insert newNode after last
    {
        last->link = newNode; //insert newNode after last
        last = newNode; //make last point to the actual
                        //last node in the list
        count++;        //increment count
    }
}

2.3.4 Delete a node

Next, we discuss the implementation of the member function deleteNode, which deletes a node from the list with a given info. We need to consider the following cases:

Suppose that the node to be deleted is 37:

Delete a node - delete 37

After deletion:

Delete a node - deleted 37
template <class Type>
void unorderedLinkedList<Type>::deleteNode(const Type& deleteItem)
{
    nodeType<Type> *current; //pointer to traverse the list
    nodeType<Type> *trailCurrent; //pointer just before current
    bool found;

    if (first == NULL)    //Case 1; the list is empty.
        cout << "Cannot delete from an empty list." << endl;
    else
    {
        if (first->info == deleteItem) //Case 2; the node to be
            // deleted is the first node
        {
            current = first;
            first = first->link;
            count--;

            if (first == NULL) //the list has only one node
                last = NULL;

            delete current;
        }
        else //search the list for the node with the given info
        {
            found = false;
            trailCurrent = first;  //set trailCurrent to point
            //to the first node
            current = first->link; //set current to point to
            //the second node

            while (current != NULL && !found)
            {
                if (current->info != deleteItem)
                {
                    trailCurrent = current;
                    current = current->link;
                }
                else
                    fount = true;
            }

            if (found) //Case 3; the node to be deleted is somewhere
                //in the list, not the last node
            {
                trailCurrent->link = current->link;
                count--;

                if (last == current) //Case 3; node to be deleted was
                    //the last node
                    last = trailCurrent; //update the value of last

                delete current; //delete the node from the list
            }
            else //Case 4; the node to be deleted is not in the list
                cout << "The item to be deleted is not in the list." << endl;
        }//end else
    }//end else
}//end deleteNode

3. Ordered Linked Lists

We derive the class orderedLinkedList from the class linkedListType and provide the definitions of the abstract functions search, insertFirst, insertLast, and deleteNode to take advantage of the fact that the elements of an ordered linked list are arranged using some ordering criteria.

For simplicity, we assume that elements of an ordered linked list are arranged in ascending order.

Because the elements of an ordered linked list are in order, we include the function insert to insert an element in an ordered list at the proper place.

3.1 orderedLinkedList class declaration

// This class specifies the members to implement the basic
// properties of an ordered linked list. This class is
// derived from the class linkedListType.

template <class Type>
class orderedLinkedList: public linkedListType<Type>
{
public:
    bool search(const Type& searchItem) const;
    void insert(const Type& newItem);
      //Function to insert newItem in the list.
      //Postcondition: first points to the new list, newItem
      //    is inserted at the proper place in the list, and
      //    count is incremented by 1.
    void insertFirst(const Type& newItem);
    void insertLast(const Type& newItem);
    void deleteNode(const Type& deleteItem);
};

3.2 The UML class diagram of the class orderedLinkedList and the inheritance hierarchy

UML class diagram of the class orderedLinkedList

3.3 The definitions of the member functions of the class orderedLinkedList

 3.3.1 Search the List

Because the list is sorted, we can improve the search algorithm somewhat. As before, we start the search at the first node in the list. We stop the search as soon as we find a node in the list with info greater than or equal to the search item, or we have searched the entire list.

The following steps describe this algorithm:

  1. Compare the search item with the current node in the list. If the info of the current node is greater than or equal to the search item, stop the search; otherwise, make the next node the current node.
  2. Repeat Step 1 until either an item in the list that is greater than or equal to the search item is found, or no more data is left in the list to compare with the search item.

Note that the loop does not explicitly check whether the search item is equal to an item in the list. Thus, after the loop executes, we must check whether the search item is equal to the item in the list.

template <class Type>
bool orderedLinkedList<Type>::search(const Type& searchItem) const
{
    bool found = false;
    nodeType<Type> *current; //pointer to traverse the list

    current = first; //start the search at the first node

    while (current != NULL && !found)
    {
        if (current->info >= searchItem)
            found = true;
        else
            current = current->link;
    }

    if (found)
        found = (current->info == searchItem); //test for equality

    return found;
}

3.3.2 Insert a Node

To insert an item in an ordered linked list, we first find the place where the new item is supposed to go, then we insert the item in the list. To find the place for the new item in the list, as before, we search the list. Here we use two pointers, current and trailCurrent, to search the list. The pointer current points to the node whose info is being compared with the item to be inserted, and trailCurrent points to the node just before current. Because the list is in order, the search algorithm is the same as before.

template <class Type>
void orderedLinkedList<Type>::insert(const Type& newItem)
{
    nodeType<Type> *current; //pointer to traverse the list
    nodeType<Type> *trailCurrent; //pointer just before current
    nodeType<Type> *newNode; //pointer to create a node

    bool found;

    newNode = new nodeType<Type>; //create the node
    newNode->info = newItem; //store newItem in the node
    newNode->link = NULL; //set the link field of the node to NULL

    if (first == NULL)  //Case 1: The list is initially empty
    {
        first = newNode;
        last = newNode;
        count++;
    }
    else
    {
        current = first;
        found = false;

        while (current != NULL && !found) //search the list
        {
            if (current->info >= newItem)
                found = true;
            else
            {
                trailCurrent = current;
                current = current->link;
            }
        }

        if (current == first) //Case 2: insert in the begining
        {
            newNode->link = first;
            first = newNode;
            count++;
        }
        else //Case 3
        {
            trailCurrent->link = newNode;
            newNode->link = current;

            if (current == NULL)
                last = newNode;

            count++;
        }
    }//end else
}//end insert

3.3.3 Insert First and Insert Last

The function insertFirst inserts the new item at the beginning of the list. However, because the resulting list must be sorted, the new item must be inserted at the proper place. Similarly, the function insertLast must insert the new item at the proper place. We, therefore, use the function insert to insert the new item at its proper place.

Note that in reality, the functions insertFirst and insertLast do not apply to an ordered linked list because the new item must be inserted at the proper place in the list. However, you must provide its definition as these functions are declared as abstract in the parent class (linkedListType).

template <class Type>
void orderedLinkedList<Type>::insertFirst(const Type& newItem)
{
    insert(newItem);
}

template <class Type>
void orderedLinkedList<Type>::insertLast(const Type& newItem)
{
    insert(newItem);
}

3.3.4 Delete a Node

To delete a given item from an ordered linked list, first we search the list to see whether the item to be deleted is in the list. The function to implement this operation is the same as the delete operation on general linked lists. Here, because the list is sorted, we can somewhat improve the algorithm for ordered linked lists.

As in the case of insertNode, we search the list with two pointers, current and trailCurrent. Similar to the operation insertNode, several cases arise:

template <class Type>
void orderedLinkedList<Type>::deleteNode(const Type& deleteItem)
{
    nodeType<Type> *current; //pointer to traverse the list
    nodeType<Type> *trailCurrent; //pointer just before current
    bool found;

    if (first == NULL) //Case 1:The list is initially empty
        cout << "Cannot delete from an empty list." << endl;
    else
    {
        current = first;
        found = false;

        while (current != NULL && !found) //search the list
            if (current->info >= deleteItem)
                found = true;
        else
        {
            trailCurrent = current;
            current = current->link;
        }

        if (current == NULL) //Case 4: deleteItem is not in the list
            cout << "The item to be deleted is not in the list."
                 << endl;
        else
        {
            if (current->info == deleteItem) //deleteItem is in the list
            {
                if (first == current) //Case 2: deleteItem is
                                      //contained in the first node
                {
                    first = first->link;

                    if (first == NULL)
                        last = NULL;

                    delete current;
                }
                else  //Case 3: deleteItem is somewhere in the list
                {
                    trailCurrent->link = current->link;

                    if (current == last)
                        last = trailCurrent;

                    delete current;
                }
                count--;
            }
            else  //Case 4: deleteItem is not in the list
                cout << "The item to be deleted is not in the "
                     << "list." << endl;
        }
    }
}