Introduction To Linked Lists

A list is a ordered sequence of objects or data values. Lists represent a very powerful and flexible means of organizing data. Lists are so flexible that it can be a problem. You will see many different uses of lists in programs all just slightly different. Lists are used in all programming languages - not just object-oriented ones. It helps to begin with an example of simple lists of animal names and then later see how these ideas can be encapsulated using classes.

Consider the following class which is part of a larger program which is presented below.

    class Node<T> { // a Node holding a value of type T and a next reference
     T valu;   Node<T> next; 
     public Node( T s, Node<T> n) { valu = s; next = n; }
    }
Note that a Node object consists of a valu and a reference to another (next) Node. The next Node will contain a reference to another one and so on. The last Node in a list will have a null next reference which has the effect of terminating the list.

Now consider the following code which creates three Node objects each containing a reference to the previous one.

      Node tail = new Node( "fox", null);
      Node mid  = new Node( "dog", tail );
      Node head = new Node( "bat", mid);
      


This is a simple list of three valus(bat, dog, fox). The tail Node has null for a next reference terminating the sequence.

Now consider the following loop which prints out all the valus in the list in order from head to tail:

      for( Node p = head; p != null; p = p.next )
        System.out.print( p.valu + " " );
      System.out.println();  

In this loop the variable p is assigned the reference values (head, mid, tail, null). When p == null the loop terminates. The effect is to print out the valus:( bat, dog, fox ) each followed by a space. In the complete program which appears below this loop is written as a method called output.

Lists are extremely flexible. It is a simple matter to insert a new Node at the end or in the middle or at the front of a list:

 

      tail.next = new Node( "gnu", null);
      mid .next = new Node( "emu", mid.next);
      head.next = new Node( "cat", head.next);
      head      = new Node( "ape", head);

 

If we now output the list using the same loop as before we will get seven animals valus ( ape bat cat dog emu fox gnu ).

It is also a simple matter to delete nodes from a list:

      mid.next = mid.next.next;  // delete valu after dog
Now the output would be: ( ape bat cat dog fox gnu ). Note that the emu Node has been deleted. In most languages such as C++ we would have to be very careful to recycle the memory containing Nodes which are removed from a list. Java has a "garbage collection" process which will find such deleted objects and make the memory available for new objects. This simplifies the programmer's job considerably.

One of the peculiar features of lists is that it is most convenient to construct them "right to left" with the last valu to be inserted appearing first in the list. It is not hard to make a copy of a list in reverse order:

      Node rev = null;
      for( Node p = head; p != null; p = p.next )
        rev = new Node( p.valu, rev );
If we output the list beginning with rev we get: gnu fox dog cat bat ape.

It is also possible to construct a list from "left to right" if you are careful to keep track of the Node which is currently last in the list. The following code shows how to read valus from standard input and store them in the same order in a list whose first Node is stored in the reference variable inList:

      
    Scanner sc = new Scanner( System.in );
    String line;
    Node inList = null;
    if ( sc.hasNextLine() ) {
      inList = new Node( sc.nextLine(), null), last = inList;
      while ( sc.hasNextLine() )
        last = last.next = new Node( sc.nextLine(), null );
The last line contains two assignment statements. In this case the order of the assignments matters(right to left)!

All these code fragments are gathered together into a file Animals.java which you may wish to compile and run.

Exercises

  1. Write a method to calculate the current length of a list.
  2. Write a method to test if a particular valu occurs in a given list.
  3. Write a method to test if a list of valus is in alphabetical order. (If you are "into recursion" it is possible to write this method as just one long statement!)
  4. Write a method to insert a valu into an ordered list so that it remains ordered.
  5. Write a method to traverse a given list inserting each valu into a second list (initially empty) ending up with a sorted copy of the list. This is called insertion sort. Show that it is O(n*n) where n is the length of the given list.
  6. Modify the given program to read in lines of text containing a valu followed by a semicolon and then an address and store the information in a list where the valus and addresses are stored as separate strings. There are two ways to do this. Class Node could have an address field added to it. This is proably the simplest. An alternative approach would be to package valus and addresses as some new kind of object and replace the valu field in class Node by this new kind of object. This is more work but may lead to a better program. Try it both ways. In either case you must be able to print out the list.
  7. Add a method to the previous exercise to check if more than one person in the list has the same address.
  8. Write a method to merge two alphabetically sorted lists of valus into one combined list.
  9. Write a method to create a list containing the 1st, 3rd, 5th, ... elements of another list.
  10. Write a method to create a list containing the 2nd, 4th, 6th, ... elements of another list.
  11. Write a method which uses the last three methods and performs a merge sort of a given list by splitting it into odd and even sublists and sorting each of them and merging the results into one combined list. This is known as merge sort. It is O(n log n) which is dramatically faster than insertion sort for long lists. Be careful not to use recursion in the case that that there is only one valu in the list.
  12. It is possible that there not be any lines of input. How should the Scanner example be modified to cope with this situation?
  13. Suppose you have file and each line of the file has the valus of two people who are friends(separated by a comma). For example,
        Kevin, Mike
        Denis, Kevin
        Carolyn, Kevin
        ...
    
    Write a program to read such data and print out in alphabetical order the names of all the people one per line each followed by the names of the people they are friends with (also ordered). E.g.:
        Carolyn: Kevin
        Denis: Kevin
        Kevin: Carolyn, Denis, Mike
        Mike: Kevin
    
    
    To do this you will need a list of person objects each of which has a list of friend objects.