Be aware that this is a first-draft document. Please report any bugs to fleck@cs.hmc.edu.
You have seen methods in Java that call other methods. It is also possible for a method to call itself. Such methods are called "recursive."
Consider the following method:
// computes k!
public static long factorial (long k){
if (k == 1) return 1;
else
return k * factorial(k-1);
}
This method computes the factorial function, just like the method you wrote in homework 7, but in a different way. This new method computes the value of k! using the value of (k-1)!. That is, it uses the equation k! = k*(k-1)!.
At some point, a recursive method must stop calling itself and return an explicit value. Otherwise, the program would never halt. The factorial method stops calling itself when the input is 1 and returns the explicit value 1. Your code for a recursive function should first handle this "base" case and then (if the base case doesn't apply) call itself.
Recurse methods are intimately related to inductive proofs. If you have not already seen inductive proof in your math courses, you will probably see it soon.
Recursive methods can call themselves in a wide variety of ways. For example, the Fibonacci numbers F(i) are defined by the equations
So a method to compute the kth Fibonacci number would look like:
// computes the kth fibonacci number
public static long fibonacci (long k){
if (k == 1 || k == 2) return 1;
else
return fibonacci (k-1) + fibonacci(k-2);
}
A major disadvantage of arrays is that you must know the number of values to be stored before you create the array. This information is not always available in advance. For example, in the traffic problem of homework 6, we did not know how many values the user might input. Therefore we were forced to process values as they came in. This forced our analysis code to have complex structure and limited what it could do, e.g. it could not print out a copy of the input data after the analysis was finished.
Linked lists are the simplest way to manipulate variable-length lists of data. (Other techniques are covered in later CS courses.) A linked list consists of a series of "nodes." Each node contains two items: a data value and the address of the next node in the series ("next").
For example, here is a picture of a linked list with three nodes:
value: keller |--> value: hodas |--> value: hadas next: -----------| next: -----------| next: null
The special value null is used to represent the empty list (list with no elements in it). It is also used to fill the next field of the last node in a linked list.
The easiest way to manipulate a linked list is to add and remove elements at the front of the linked list. For example, you can make a linked list by starting with an empty list (null) and repeatedly adding new nodes to the start of the list. Here is a sample program that uses this method to build a linked list with five elements.
================================================================
// Building and using a linked list.
// Margaret Fleck
// 26 November 1999
import HMC.HMCSupport;
class Node {
// Each node contains a data value and the address of
// the next node in the linked list.
String data;
Node next;
// The constructor for a node sets its data field.
// The next field is set to the special value null so it
// isn't left set to some random unknown value.
Node (String Data) {
data = Data;
next = null;
}
// Print method for the linked list class
public static void print(Node input){
Node mynode = input;
HMCSupport.out.print("[ ");
while (mynode != null){
HMCSupport.out.print(mynode.data + " ");
mynode = mynode.next;
}
HMCSupport.out.println("]");
}
// Returns the list with a new value stuck on the front
public static Node cons (Node inputlist, String newvalue){
Node newnode = new Node (newvalue);
newnode.next = inputlist;
return newnode;
}
// Silly main method to test list handling methods.
public static void main (String args[]){
// Some test data.
String testdata [] = {"keller", "hodas", "hadas", "eckert", "fleck"};
// Make a linked list containing the test data, by starting
// with an empty list and repeatedly pushing values onto the
// front of the list.
Node testlist = null;
for (int i = 0; i < 5; i++){
testlist = cons(testlist,testdata[i]);
}
// Print the test list.
print(testlist);
}
}
================================================================
The program's output looks like:
[ fleck eckert hadas hodas keller ]
A variable that "contains a linked list" actually contains the first node of that list. So, for example, the print method is passed the first node of the test list.
Notice how the print method walks down the linked list, using a while loop. The loop variable is initially set to the first node in the list. Each time the loop runs, the loop variable is reset to the next node in the list. The next node is the value in the next field of the current node. The loop ends when the loop variable becomes null. Many methods manipulating linked lists use similar loops.
Also notice that the output of the cons method is a linked list that shares most of its nodes with the input linked list. It would be possible for this method to make new nodes for the output list, each a copy of the corresponding node of the input list. However, this would make the cons method harder to write and run more slowly. In many applications, it is fine for two linked lists to share nodes.
Since the linked list was built by repeatedly pushing values onto the front of the list, the output list contains the values in reverse order. We can reverse the order by creating a new list and pushing the values from the first list onto the front of the new list.
// Return a reversed copy of the input list.
public static Node reverse(Node input){
Node output = null;
Node ptr = input;
Node newnode;
while (ptr != null){
newnode = new Node(ptr.data);
newnode.next = output;
output = newnode;
ptr = ptr.next;
}
return output;
}
The same technique of walking down the list can be used to return the last node in the last or the last value in the list, as in the following methods:
// Return last value in linked list.
// Assumes input is never null.
public static String last(Node input){
Node ptr = input;
Node newnode;
while (ptr.next != null){
ptr = ptr.next;
}
return ptr.data;
}
// Return last value in linked list.
// Assumes input is never null.
public static Node lastNode(Node input){
Node ptr = input;
Node newnode;
while (ptr.next != null){
ptr = ptr.next;
}
return ptr;
}
The above code used static methods to manipulate linked lists. Static methods could also be used, but they would not be able to take the empty list (null) as input. The problem is that the magic variable null does not belong to the list class but can act as a member of any class. So if we were to write null.print(), java would not know which print function we meant to call.
Another approach to creating linked lists is to use two classes: Node and List. The List class is simply a holder for the first node in the list. Implementing lists this way is slightly more complicated. However it allows list manipulation methods to be coded as non-static methods (in the List class).
Many methods for manipulating linked lists can easily be written as recursive methods. For example, the lengthh of a linked list can be computed as follows:
// computes length of list
public static int length (Node inputlist){
if (inputlist == null) return 0;
else
return 1+length(inputlist.next);
}
Similarly, the following two methods print the contents of a linked list. The print method prints the brackets around the linked list and calls a recursive helper method which prints the values in the list. This is a very common coding pattern.
// helper for print method
public static void printhelper (Node input){
if (input != null){
HMCSupport.out.print(input.data + " ");
printhelper(input.next);
}
}
// Print function for the linked list class
public static void print(Node input){
Node mynode = input;
HMCSupport.out.print("[ ");
printhelper(input);
HMCSupport.out.println("]");
}
Recursive functions can also be used to search through a linked list, as in the following function which looks for negative numbers.
// Does list contain any negative numbers?
public static boolean anyNegs (Node input){
if (input == null) return false;
else
return ((input.data < 0) || anyNegs(input.next));
}
Last modified Nov 24 199 for Fall 99 cs5 by fleck@cs.hmc.edu