Лучший способ решить эту проблему - нарисовать несколько картинок. Затем попробуйте разбить проблему на подзадачи. Начнем с простого случая: список длиной 2:
ListItem two = new ListItem(1, ListItem(2, null));
Вот одна картинка
two = ( number == 1
( next == ( number == 2
( next == null
Вот еще одна картинка:
+---+ +---+ The "/" here is the "null" above, which terminates the list.
| 1 |->| 2 |-/
+---+ +---+
Думайте об этом так: список состоит из первого ListItem, который указывает на остальную часть списка через «next». Пустой список, тогда является нулем, и "следующий" из последнего ListItem всегда пуст. (Нуль).
Теперь, что на самом деле происходит, когда нас просят «растянуть» список? Скажем, на 2?
Ну, пустой список прост, он не меняется. Но это также не имеет значения, поскольку null.stretch()
плохо кончится на языке, который вы используете. Список длины 1 в нашем простейшем практическом случае:
у нас есть:
we have we want
+---+ +---+ +---+
| 1 |-/ | 1 |-->| 1 |-/
+---- +---+ +---+
Хорошо, это не так сложно. У нас уже есть список длины один. Все, что нам нужно сделать, это повесить его на следующий новый элемент ListItem, и у нас будет список длиной два. Очевидно, нам нужна возможность добавить что-то в существующий список. Добавить его в начало проще всего, поэтому для этого мы определим небольшого помощника:
ListItem addItemToFront(int number) {
return new ListItem(number, this);
}
Хорошо, теперь давайте закодируем это и назовем его stretchFirstItemByOne:
ListItem stretchFirstItemByOne() {
return this.addItemToFront(this.number);
}
Вы увидите, что я часто использую this.something () в этих примерах,
хотя это не обязательно. Я просто пытаюсь понять, что
это вызовы метода для текущего объекта (this).
Но, предположим, мы хотим растянуть на несколько больших n ? Вы уже пытались - несколько неудачно - использовать цикл for выше. Вы могли бы сделать это. Но я собираюсь сделать это по-другому.
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();
}
}
Остановись и подумай об этом. Перепишите его как цикл for, если у вас возникли проблемы.
Это все очень хорошо, вы могли бы сказать, но он обрабатывает только списки длины один. Как верно, как верно.
Предположим, у вас есть список длины 3, и вы хотите растянуть его на 2.
+---+ +---+ +---+
( | 1 |->| 2 |->| 3 |-/ ).stretch(2)
+---+ +---+ +---+
Tough? Ну, мы можем начать по крайней мере. Мы знаем, как обращаться с вещами, если в списке есть только один элемент:
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.
}
}
Эй, но разве растянуть не должно это сделать для нас, вы знаете, растянуть целые списки? Разве мы не можем использовать это, чтобы растянуть остальную часть списка, чтобы мы могли немного упростить и растянуть первый элемент? Но мы еще не закончили писать - я имею в виду - это не работает. Это не могло быть так просто, не так ли? Может ли это?
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(...); //-------------------------
}
}