The standard way to proces a singly linked list is to traverse its cells one by one and compute on each one. Once again, here is class Cell:
public class Cell { private Object val; // value in the cell private Cell next; // the address of the next cell in the list public Cell(Object value, Cell link) { val = value; next = link; } public Object getVal() { return val; } public Cell getNext() { return next; } public void setNext(Cell link) { next = link; } }and here is the standard iterative pattern for traversing and processing all the cells in a list, l:
Cell current = l; while ( current != null ) { "process Cell current"; current = current.getNext(); }
We now use the iterative pattern in a method that counts the number of cells in a linked list. Let l be the address of the leading cell in the list to be counted:
/** lengthOf calculates the number of cells in a linked list * @param l - the leading cell of the list * @return the length of the linked list */ public int lengthOf(Cell l) { int length = 0; Cell c = l; while ( c != null ) // invariant: // lengthOf(l) == length + lengthOf(c) { length = length +1; c = c.getNext(); } return length; }The loop uses variable c to ''leap'' from cell to cell, using the links held in the cells. Variable length is incremented each time c makes a leap.
The loop makes good sense to us, because we think of a linked list as a sequence of distinct cells that are linked together:
front Cell--+---+ Cell--+---+ Cell--+------+ | o-|--> | "a" | o-|--> | "b" | o-|--> | "c" | null | +---+ +-----+---+ +-----+---+ +-----+------+So, it is easy to see that lengthOf(front) returns 3 as its answer.
In the above coding of lengthOf, there is an invariant assertion inserted inside the while loop. Recall that an invariant documents what the loop is doing while it iterates its body over and over.
Take a close look at it:
// invariant: lengthOf(l) == length + lengthOf(c)(Remember--- l is the front cell of the entire list, and c is the cell reached part way through the traversal.) What does it say?
This invariant suggests that there is a recursion in the basic notion of traversing the loop. Indeed, because class Cell is defined in terms of itself, a simpler solution to the above problem is written with a recursively defined method:
/** lengthOf calculates the number of cells in a linked list * @param l - the leading cell of the list * @return the length of the linked list */ public int lengthOf(Cell l) { int length = 0; if ( l == null ) { length = 0; } // an empty list has length 0 else // the list is nonempty, so count its cells: { // Calculate the length of the list that // follows the lead cell, l: Cell suffix = l.getNext(); length = lengthOf(suffix); // now, add one for l's cell: length = length + 1; } return length; }The method is based on the natural and obviously correct notion that the length of a linked list is the length of the lead cell (namely, 1) added to length of the rest of the list.
Here is a more terse restatement of the method:
public int lengthOf(Cell l) { int length = 0; if ( l == null ) { length = 0; } else { length = 1 + lengthOf(l.getNext()); } return length; }Remember that when a method ``restarts'' itself via a recursion, a new copy of the method is activated. This idea is developed in the next lecture.
The recursion in lengthOf suggests that a list is a ``nested'' or ``layered'' structure, which we might draw like this:
Cell-----------------------+ | "a" | | Cell--------------+ | | | "b" | | | | Cell-------+ | | | | | "c" | | | | | | null | | | | | +----------+ | | | +-----------------+ | +--------------------------+The ``layers'' are implied by the internal structure of class Cell:
public class Cell { private Object value; private Cell next; // this is another, smaller list embedded in this one ... public Object getVal() { return value; } public Object getnext() { return next; } }Notice the ``recursive'' reference to Cell within class Cell --- it suggests that another list is nested or layered inside the Cell that holds the front value.
Of course, we have studied heap-storage layout well enough to know that field next holds the address of an object that happens to be also a Cell object. But the idea is intriguing: In concept, can a Cell hold inside itself another Cell? If we answer ``yes'' to this question, we move from the world of ``iterative'' data structures to the world of ``recursive'' (``inductive'') ones.