33A. Destructive Functions on Linked Lists


Destructive and nondestructive functions

The list functions that we have written so far have been nondestructive, meaning that they do not alter a list. Now we turn to destructive algorithms, which do alter lists, either by changing the numbers in the list cells or by changing the list structure by altering the pointers.


Example: insert a value into a sorted linked list

Imagine that you have a linked list L that is in nondescending order. For example, list [2, 4, 4, 5, 9] is in nondescending order, but [2, 4, 3] is not. You would like to insert a value into that list, changing the list, and preserving the fact that it is in nondescending order. For example, if L is [2, 4, 4, 5, 9] before inserting 7, then L is [2, 4, 4, 5, 7, 9] after inserting 7.

When thinking about destructive functions, it is best to look at linked list diagrams rather than only looking at conceptual lists. Starting with linked list

and inserting 7 leads to the following linked list.

Notice that one list cell has been created to hold 7.

This function needs to be able to change the pointer in L so that it can insert a new cell, as can be seen from cases 1 and 2 below. So L needs to be passed by reference. There are three cases to consider to insert x into L.

  1. If L is an empty list, just change L to [x] = cons(x, NULL).

  2. If L is not empty and its head is greater than or equal to x, then x belongs at the beginning of the list; change L to cons(x,L). For example, if x is 1 and L is [2, 4, 6, 8], change L to be [1, 2, 4, 6, 8].

  3. If L is not empty and its head is less than x then insert x into the tail of L. The change in pointers in the tail of L will affect L.

Notice that the first two cases can both be accomplished by statement
  L = cons(x, L);
since, in case 1, L is NULL. Coding those ideas in C++ yields the following definition of insert.
  // Insert(x,L) inserts x into list L, changing L.
  //
  // L must be in nondescending order when insert is called,
  // and x is inserted in the correct place so that
  // L remains in nondescending order after the insertion.

  void insert(const int x, List& L)
  {
    if(L == NULL || x <= L->head)
    {
      L = cons(x, L);
    }
    else
    {
      insert(x, L->tail);
    }
  }
Notice that the definition of insert is tail-recursive. An optimizing compiler can change it into a loop for you.


Pointer variables

Suppose that line

      insert(x, L->tail);
in the definition of insert is replaced by the following line.
      insert(x, tail(L));
That does not work. The problem is that the second parameter of insert uses call-by-reference. That means the actual thing that is passed must be a variable. L->tail is a variable, but tail(L) is not.


Exercises

  1. Write a C++ definition of function removeAll(x, L), which removes all occurrences of x from list L, changing L. Answer

  2. Write a C++ definition of destructive function addToEnd(x, L), which adds x to the end of list L. Answer

  3. Write a C++ definition of destructive function addToStart(x, L) that adds x to the beginning of list L. If L has length n, how long does it take to perform addToStart(x, L)? Answer

  4. Write a C++ definition of function destroy(L), which deletes every cell in list L. Answer