February 1, 2019

Leetcode.com, задача №206. "Переворот связного списка".


!!НАШ БЛОГ ПЕРЕЕХАЛ!!

Мы создали свой сайт! Все материалы, опубликованные в этом блоге, переехали туда.

Наш новый сайт maddevelop.ru

А данную статью вы можете найти по ссылке


Условие

Отобразите в обратном направлении простой связный список.

Пример

Входной список: 1 -> 2 -> 3 -> 4 -> 5 -> null

Выходной список: 5 -> 4 -> 3 -> 2 -> 1 -> null

Итеративный вариант решения

Немного теории.

Связный список — базовая динамическая структура данных в информатике, состоящая из узлов, каждый из которых содержит как собственно данные, так и одну или две ссылки («связки») на следующий и/или предыдущий узел списка.

В нашем случае мы имеем дело с простым связным списком, т.е. каждый узел содержит ссылку на один последующий:

public class ListNode
    {
        public int val;                     // поле, хранящее значение начала списка
        public ListNode next;               // поле, указывающее на всю последующую часть
        public ListNode(int x) { val = x; } // конструктор 
    }

Создать входной связный список из примера можно, например, так:

ListNode ssp = new ListNode(1);
ssp.next = new ListNode(2);
ssp.next.next = new ListNode(3);
ssp.next.next.next = new ListNode(4);
ssp.next.next.next.next = new ListNode(5);
ssp.next.next.next.next.next = null;

Итеративный подход (англ. iteration - «повторение») в разработке программного обеспечения — это выполнение работ параллельно с непрерывным анализом полученных результатов и корректировкой предыдущих этапов работы.

Код метода реверса связного списка:

public static ListNode ReverseList(ListNode head)
        {
            ListNode prev = null;
            ListNode curr = head;
            while (curr != null)
            {
                ListNode nextTemp = curr.next;
                curr.next = prev;
                prev = curr;
                curr = nextTemp;
            }
            return prev;
        }

До тех пор, пока не достигнем окончания списка мы на каждой итерации будем выполнять четыре операции:

  • Запоминать ссылку на следующий узел.
ListNode nextTemp = curr.next;
  • В текущем списке ссылку на следующий узел заменять на скорректированную.
curr.next = prev;
  • Запоминать текущее состояние списка.
prev = curr;
  • Переходить к следующему узлу.
curr = nextTemp;

Посмотрим, как работает алгоритм для связного списка 1 -> 2 -> 3 -> null.

Итерация №1

  nextTemp = curr.next = 2 -> 3 -> null
  curr.next = prev = null
  prev = curr = 1 -> null
  curr = 2 -> 3 -> null

Итерация №2

  nextTemp = curr.next = 3 -> null
  curr.next = prev = 1 -> null
  prev = curr = 2 -> 1 -> null
  curr = 3 -> null

Итерация №3

  nextTemp = curr.next = null
  curr.next = prev = 2 -> 1 -> null
  prev = curr = 3 -> 2 -> 1 -> null
  curr = null

Т.к. curr = = null, то произойдёт выход из цикла.

Временная сложность: n, т.к. количество итераций зависит от количества узлов списка.

Пространственная сложность: 1, т.к. мы используем лишь дополнительные переменные, кроме основного других циклов нет.

Рекурсивный вариант решения

Код решения с рекурсией выглядит таким образом:

  public static ListNode ReverseList(ListNode head)
  {
    if (head == null || head.next == null)
      retutn head;
    ListNode p = ReverseList(head.next);
    head.next.next = head;
    return p;        
  }

Рассмотрим действие алгоритма на примере списка 1 -> 2 -> 3 -> null.

Первый (основной) вызов метода
ReverseList(1 -> 2 -> 3 -> null)
{
  ListNode p = ReverseList(2 -> 3 -> null)
  Происходит второй вызов метода.
    ListNode p = ReverseList(3 -> null);
    Происходит третий вызов метода.
      Третий вызов возвращает 3 -> null, т.к. head.next == null.
    Получаем, что ListNode p = 3 -> null. 
    В начале второго вызова head = 2 -> 3 -> null.
    head.next.next = head
    Получаем, что head = 2 -> 3 -> 2 -> 3 -> null.
    При этом p = 3 -> 2 -> 3 -> null
        head.next = null
    Таким образом, head = 2 -> null, a p = 3 -> 2 -> null.
    Второй вызов возвращает 3 -> 2 -> null.
  При возврате к первому методу head = head -> head.next = 1 -> 2 -> null
  ListNode p = 3 -> 2 -> null.
  head.next.next = head = 1 -> 2 -> 1 -> 2 -> null
  При этом p = 3 -> 2 -> 1 -> 2 -> 1 -> 2 -> null 
  head.next = null = 1 -> null
  p, оно же и ответ, становится равным 3 -> 2 -> 1 -> null  
}

Временная сложность: n, по-прежнему зависит от длины списка.

Пространственная сложность: n, т.к. равняется глубине рекурсии.



Ещё больше интересной информации на нашем Telegram канале.