4.3 KiB
Вывод списка в цикле
//+ run
var list = {
value: 1, next: {
value: 2, next: {
value: 3, next: {
value: 4, next: null
}
}
}
};
function printList(list) {
var tmp = list;
while(tmp) {
alert( tmp.value );
tmp = tmp.next;
}
}
printList(list);
Обратите внимание, что для прохода по списку используется временная переменная tmp
, а не list
. Можно было бы и бегать по списку, используя входной параметр функции:
function printList(list) {
while(*!*list*/!*) {
alert( list.value );
list = list.next;
}
}
...Но при этом мы в будущем не сможем расширить функцию и сделать со списком что-то ещё, ведь после окончания цикла начало списка уже нигде не хранится.
Поэтому и используется временная переменная -- чтобы сделать код расширяемым, и, кстати, более понятным, ведь роль tmp
-- исключительно обход списка, как i
в цикле for
.
Вывод списка с рекурсией
Рекурсивный вариант printList(list)
следует простой логике: вывести текущее значение (1)
, а затем пропустить через себя следующее (2)
:
//+ run
var list = {
value: 1, next: {
value: 2, next: {
value: 3, next: {
value: 4, next: null
}
}
}
};
function printList(list) {
alert(list.value); // (1)
if (list.next) {
printList(list.next); // (2)
}
}
printList(list);
Обратный вывод с рекурсией
Обратный вывод -- почти то же самое, что прямой, просто сначала мы обрабатываем следующее значение, а потом -- текущее:
//+ run
var list = {
value: 1, next: {
value: 2, next: {
value: 3, next: {
value: 4, next: null
}
}
}
};
function printReverseList(list) {
if (list.next) {
printReverseList(list.next);
}
alert(list.value);
}
printReverseList(list);
Обратный вывод без рекурсии
//+ run
var list = {
value: 1, next: {
value: 2, next: {
value: 3, next: {
value: 4, next: null
}
}
}
};
function printReverseList(list) {
var arr = [];
var tmp = list;
while(tmp) {
arr.push(tmp.value);
tmp = tmp.next;
}
for( var i = arr.length-1; i>=0; i-- ) {
alert( arr[i] );
}
}
printReverseList(list);
Обратный вывод без рекурсии быстрее.
По сути, рекурсивный вариант и нерекурсивный работают одинаково: они проходят список и запоминают его элементы, а потом выводят в обратном порядке.
В случае с массивом это очевидно, а для рекурсии запоминание происходит в стеке (внутренней специальной структуре данных): когда вызывается вложенная функция, то интерпретатор сохраняет в стек текущие параметры. Вложенные вызовы заполняют стек, а потом он выводится в обратном порядке.
При этом, при рекурсии в стеке сохраняется не только элемент списка, а другая вспомогательная информация, необходимая для возвращения из вложенного вызова. Поэтому тратится больше памяти. Все эти расходы отсутствуют во варианте без рекурсии, так как в массиве хранится именно то, что нужно.
Преимущество рекурсии, с другой стороны -- более короткий и, зачастую, более простой код.