158 lines
No EOL
4.4 KiB
Markdown
158 lines
No EOL
4.4 KiB
Markdown
# Вывод списка в цикле
|
||
|
||
```js
|
||
//+ 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`. Можно было бы и бегать по списку, используя входной параметр функции:
|
||
|
||
```js
|
||
function printList(list) {
|
||
|
||
while(*!*list*/!*) {
|
||
alert( list.value );
|
||
list = list.next;
|
||
}
|
||
|
||
}
|
||
```
|
||
|
||
...Но при этом мы в будущем не сможем расширить функцию и сделать со списком что-то ещё, ведь после окончания цикла начало списка уже нигде не хранится.
|
||
|
||
Поэтому и используется временная переменная -- чтобы сделать код расширяемым, и, кстати, более понятным, ведь роль `tmp` -- исключительно обход списка, как `i` в цикле `for`.
|
||
|
||
# Вывод списка с рекурсией
|
||
|
||
Рекурсивный вариант `printList(list)` следует простой логике: вывести текущее значение `(1)`, а затем пропустить через себя следующее `(2)`:
|
||
|
||
```js
|
||
//+ 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);
|
||
```
|
||
|
||
# Обратный вывод с рекурсией
|
||
|
||
Обратный вывод -- почти то же самое, что прямой, просто сначала мы обрабатываем следующее значение, а потом -- текущее:
|
||
|
||
```js
|
||
//+ 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);
|
||
```
|
||
|
||
# Обратный вывод без рекурсии
|
||
|
||
```js
|
||
//+ 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);
|
||
```
|
||
|
||
**Обратный вывод без рекурсии быстрее.**
|
||
|
||
По сути, рекурсивный вариант и нерекурсивный работают одинаково: они проходят список и запоминают его элементы, а потом выводят в обратном порядке.
|
||
|
||
В случае с массивом это очевидно, а для рекурсии запоминание происходит в стеке (внутренней специальной структуре данных): когда вызывается вложенная функция, то интерпретатор сохраняет в стек текущие параметры. Вложенные вызовы заполняют стек, а потом он выводится в обратном порядке.
|
||
|
||
При этом, при рекурсии в стеке сохраняется не только элемент списка, а другая вспомогательная информация, необходимая для возвращения из вложенного вызова. Поэтому тратится больше памяти. Все эти расходы отсутствуют во варианте без рекурсии, так как в массиве хранится именно то, что нужно.
|
||
|
||
Преимущество рекурсии, с другой стороны -- более короткий и, зачастую, более простой код. |