If you want to implement your solutions in a naturally recursive way, assume that your LinkedList<E> implementation has the following constructor and methods (not tested... should be close to correct):
// This lets us construct non-empty lists.
// Contrast with setting first = null.
public LinkedList<E>(ListNode<E> ln) {
first = ln;
}
// Returns the first item in the list.
public E first() {
if (first != null) {
return (E)first.getItem();
} else { ... exception ... }
}
public LinkedList<E> rest() {
// Constructs a new, non-empty LinkedList<E>
// beginning with the second node.
if (first != null)
return new LinkedList<E>(first.getNext());
} else { ... exception ... }
}
[ WARNING ] The above implementation of rest() has a fundamental problem, and in practice would cause huge problems! Can you figure out what that problem is? (Conceptually, it works just fine.)
These methods let you write code that using the LinkedList<E> like the following:
public int listSize(LinkedList<E> ls) {
if (ls.first() == null) {
return 0;
} else {
return 1 + listSize(ls.rest());
}
This may look like your size() implementation, but instead of being written as a method of the class in terms of ListNode<E> objects, this is written as an external method in terms of LinkedList<E>s.
As always, you may write your solutions in terms of loops. However, all of these can be written if you follow the definition of a list.