CSE143 Notes 4/10/06

Algorithms on Linked Lists

Review: here the representation we've been using for links in Java.

  public class Link {        // a single node in a linked list
    public Object item;      // data item referenced by this link
    public Link   next;      // next node in the list or null if no next node


    // construct a new Link with given item and next fields
    public Link(Object item, Link next) {
      this.item = item;
      this.next = next;
    }
    
    // construct a new Link without initializing any fields
    public Link() {
      this(null, null);
    }
  }

When we want to create a list, we'll create a suitable variable somewhere to refer to the first link (assuming the list is not empty), for example,

  Link head;    // reference to first node in list, or null if list is empty

Eventually, we're going to use links to create versions of data structures like our StringList, only with a linked list underneath instead of an array. But before then we need to gain some facility working with linked lists to perform various tasks.

Many algorithms for processing a linked list have the following form:

  start at the beginning (the first link)
  while we haven't reached the end of the list {
     process the data referenced by the current link
     advance to the next link
  }

We can refine this and move closer to real code by filling in some details:

    Link p = head;
  while (p != null) {

        process the data referred to by link p;
     p = p.next;
  }

This is an example of a program schema or template. It gives us a standard way of processing a list without giving the particular details of what needs to be done with the data in each node. The exact processing for each node will depend on what we are trying to do, but the overall schema will be the same regardless of what we are doing.

This sort of pattern appears often in programming: think about tasks like "go through an array and do x", or "read through the input until the end of the file and do y". In many of these cases there is a standard schema or template for the overall organization of the code, but the details will differ. Learning to recognize situations like this and adding standard ways of doing things to your toolkit is a significant part of developing into a good, experienced programmer.

Let's work out a detailed example. Suppose head points to a linked list whose nodes contain the strings "apple", "banana", and "cherry". Draw a picture of the list:

 

 

 

 

Now, let's write code to print out all of the words in the list. Since the job involves traversing a list and doing something at each node, we can start by writing down our standard schema for traversing a list:

    Link p = head;
  while (p != null) {

        process the data referred to by link p;
     p = p.next;
  }

What do we need to do to process each node? In this case, just print it! So we can substitute a println statement for the "process the data" step and we get

  Link p = head;
  while (p != null) {
     System.out.println(p.item);
     p = p.next;
  }

Does this work? How do we know? Answer: a first way is to "desk-check" our work by simulating the operation of the computer by hand. Do that here. (You'll need to draw a picture of the list showing how p is updated, as well as leave some room to write down the output that's produced.)

 

 

 

 

 

 

Is the code right? Does it work on an ordinary list? Does it work on edge cases? (What if the list is empty?)

Now let's try a couple more problems. Recall that our schema for processing a list is

    Link p = head;
  while (p != null) {

        process the data referred to by link p;
     p = p.next;
  }

Problem: Print the number of nodes in the list. What do we need to do inside the loop? What do we need to do before or after the loop?

Your code here: (and also, hand-simulate it to be sure that it works - both in the ordinary case and for unusual cases like an empty list)

 

 

 

 

 

 

 

 

Another problem: print "yes" if the list contains the string s (which we assume has been initialized somewhere else), otherwise print "no".

 

 

 

 

 

 

 

One final thought on while loops vs for loops. The Java (and C/C++) for loop can be used to write any code that can be written as a while loop. Some people have strong preferences one way or the other, but it's basically a matter of taste.