Вивести одинозв’язаний список
Скажімо, у нас є одинозв’язаний список (як описано в розділі Рекурсія та стек):
let list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
Напишіть функцію printList(list), що виводить список елементів один за одним.
Зробіть два варіанти рішення: з використанням циклу та з використанням рекурсії.
Що краще: з рекурсією чи без неї?
Рішення на основі циклу
Варіант рішення на основі циклу:
let list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
function printList(list) {
let 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;
}
}
…Але це було б нерозумно. У майбутньому нам доведеться розширити функцію, зробити щось інше з list. Якщо ми змінюємо list, то ми втрачаємо таку здатність.
Говорячи про хороші імена змінних, list тут – це сам список. Його перший елемент. І це повинно залишитися так. Це ясно і надійний.
З іншого боку, tmp використовується виключно проходу, як i у for циклі.
Рішення через рекурсію
Рекурсивний варіант printlist(list) слідує простій логіці: вивести список, який ми повинні вивести поточний елемент list, а потім зробити те ж саме дляlist.next:
let list = {
value: 1,
next: {
value: 2,
next: {
value: 3,
next: {
value: 4,
next: null
}
}
}
};
function printList(list) {
alert(list.value); // виведіть поточний елемент
if (list.next) {
printList(list.next); // зробіть те ж саме для решти списку
}
}
printList(list);
Що ж тепер краще?
Технічно цикл є більш ефективним. Ці два варіанти роблять те ж саме, але цикл не витрачає ресурси для вкладених викликів.
З іншого боку, рекурсивний варіант коротший, а іноді його легше зрозуміти.