вернуться к уроку

Вывод односвязного списка

важность: 5

Допустим, у нас есть односвязный список (как описано в главе Рекурсия и стек):

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 здесь – это сам список, его первый элемент. Так и должно быть, это просто и понятно.

С другой стороны, 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);

Какой способ лучше?

Технически, способ с циклом более эффективный. В обеих реализациях делается то же самое, но для цикла не тратятся ресурсы для вложенных вызовов.

С другой стороны, рекурсивный вариант более короткий и, возможно, более простой для понимания.