CSE143 Notes 4/14/06

Implementing Lists with Linked Lists (2)

Last time we started looking at how to implement a list of strings using a linked list underneath. Recall that the operations included the following: LinkedStringList(), add(s), add(pos, s), size(), isEmpty(), contains(s), indexOf(s), get(pos), set(pos, s), and remove(pos). First, we defined a simple data structure to represent links (nodes):

  public class StringLink {      // a single node in a linked list of strings
    public String     item;      // string referenced by this link
    public StringLink next;      // next node in the list or null if no next node

    // constructs omitted
  }

The list class itself has one basic instance variable, which is a reference to the first node in the list, or null if the list is empty.

  public class LinkedStringList {
    private StringLink first;
    ...
  }

The basic operations we started on last time were the constructor, which just sets first to null, and add, which finds the last node in the list and adds a new node referring to the new value at the end.

Let's take a look at a few more. First, indexOf(s). The only real strategy we can use here is to start at the beginning of the list and examine entries one by one, counting as we go.

   /** Return the location of s or -1 if s not found */
   public int indexOf(String s) {







   }

Method contains(s) is easy, and the same as before: return contains(s)!=-1;.

How about size()? This is another basic query. With the list representation we have so far, we need to count the number of nodes:

   public int size() {




   }

Can we make it faster? Yes, we could create another instance variable containing the size, then just return that value. That trades off extra data for time, which is often a useful thing to do, but at the expense of some extra complexity. If we introduce this additional variable, we need to be sure that we update it every time we alter the list. What needs to change in the code?

 

 

 

Another place we can trade additional data for speed is in method add(). We can introduce an additional instance variable to keep track of the last node in the list and use it to access that node directly without having to traverse the entire list. But then we need to be careful to update it in all of the edge cases - in particular, what if we're adding the first node to an empty list?

   public class LinkedStringList {
     private StringLink first;     // first node or null if list is empty
     private StringLink last;      // last node or null if list is empty
     private int size;             // number of nodes in the list


     /** construct a new empty list */
     public LinkedStringList() {




     }


     /** Method add (revised) */
     public void add(String s) {


 
 

 
 
 




     }

There are several methods that involving access to a list item at a specified position. Let's look at one of them:

   /** Return the item at the given position */
   public String get(int pos) {


 
 



   }

One thing to note here is that we need to traverse the list until we get to the right place. There is no way to directly access a link given its position, unlike the situation with an array-based list.

Besides get, there are several other methods that deal with node positions: set(pos,s), add(pos,s), and remove(pos). All of these need to access a given position in the list, but then do different things there. This suggests is that we should isolate the "find the link at location pos" into a separate method that we can then call as needed. Like all of the other methods dealing with positions, it has a precondition that the position is within the list.

   // return a reference to the link at position pos
   // pre: 0 <= pos < size()
   private StringLink getLinkAt(int pos) {






   }

Given this method, operations like get and set are easy. For example,

   /** Store s in the list at location pos */
   public void set(int pos, String s) {
     StringLink p = getLinkAt(pos);
     p.item = s;
   }

Methods add and remove are trickier. The main issue is that we don't want to find the node at the specified position. Instead, we want to find the node before the specified position, since we need to adjust its next field to insert or remove a node following the one before the given position. In addition, there are special cases if we are inserting or removing the node at position 0. The first link in the list has no previous node; instead, it is referenced directly by the list's instance variables: first (and, if we have it, last) . And if we have a last instance variable, we need to be sure it is updated if the new node is at the end of the list. Given all that, let's implement add:

   /** Insert s in the list at position pos */
   public void add(int pos, String s) {




}