The best way to approach this problem is to draw some pictures. Then try to break the problem into sub-problems. Let's start with the simple case: a list of length 2:
ListItem two = new ListItem(1, ListItem(2, null));
Here's one picture
two = ( number == 1
( next == ( number == 2
( next == null
Here's another picture:
+---+ +---+ The "/" here is the "null" above, which terminates the list.
| 1 |->| 2 |-/
+---+ +---+
Think of it this way: a list consists of a first ListItem, which points to the rest of the list via "next". The empty list, then is null and the "next" of the last ListItem is always empty. (null).
Now what's really going on when we're asked to "stretch" a list? Say, by 2?
Well, the empty list is easy, it doesn't change. But it's also irrelevant since null.stretch()
will end badly in the language you're using. The list of length 1 then is our simplest practical case:
we have:
we have we want
+---+ +---+ +---+
| 1 |-/ | 1 |-->| 1 |-/
+---- +---+ +---+
Ok, that's not so hard. We already have a list of length one. All we need to do hang it off the next of a new ListItem and we'll have a list of length two. Clearly we need the ability to add something to an existing list. Adding it to the front is easiest, so we'll define a little helper for that:
ListItem addItemToFront(int number) {
return new ListItem(number, this);
}
Ok, now let's code that up and call it stretchFirstItemByOne:
ListItem stretchFirstItemByOne() {
return this.addItemToFront(this.number);
}
You'll see me use this.something() a lot in these examples,
though it isn't necessary. I'm just trying to be clear that
these are method calls on the current object (this).
But, supposing we want to stretch by some larger n? You've already tried -- somewhat haplessly -- to use a for loop above. You could do that. But I'm going to do it differently.
ListItem stretchFirstItem(n) {
if (n == 1) // stretching to length 1 means nothing
return this; // to do. just return this.
else {
// well, if we stretch our item to length n-1 first
// then all we have to do is stretch it by one and
// we're done.
return this.stretchFirstItem(n-1).stretchFirstItemByOne();
}
}
Stop and think about that one. Rewrite it as a for loop if you're having trouble.
That's all very nice, you might say, but it only handles lists of length one. How true, how true.
Suppose you had a list of length 3 and you wanted to stretch it by 2.
+---+ +---+ +---+
( | 1 |->| 2 |->| 3 |-/ ).stretch(2)
+---+ +---+ +---+
Tough? Well, we can get started at least. We know how to handle things if the list has only one item:
ListItem stretch(int n) {
ListItem restOfList = this.next;
if (restOfList == null) { // this list has length one
return this.stretchFirstItem(n);
} else {
// if we had the rest of the list stretched, then we could
// add this.number to the front of this stretched list, stretch
// that first item and then we'd be done.
}
}
Hey, but isn't stretch supposed to do that for us, you know, stretch whole lists? Couldn't we use that to stretch the rest of the list so we can do the easy bit and stretch the first item? But we haven't even finished writing stretch yet -- I mean -- it doesn't work. It couldn't be that easy, could it? Could it?
ListItem stretch(int n) {
ListItem restOfList = this.next;
if (restOfList == null) { // this list has length one
return this.stretchFirstItem(n);
} else {
return restOfList //-------------------------
.magic(...) // Left as an exercise for
.moreMagic(...) // the reader.
.zyzzy(...); //-------------------------
}
}