стратегии реверса связанного списка в JavaScript

Я просто боролся через простой вопрос интервью: пожалуйста, переверните один связанный список.

хотя я не смог дать рабочий ответ вовремя, чтобы сохранить интервью, я смог придумать решение позже.

правильно ли мое решение? Как бы вы проанализировали это с Big-Oh? Существуют ли более эффективные способы отменить один связанный список?

// reverse a linked list

var reverseLinkedList = function(linkedlist) {
  var node = linkedlist;
  var previous = null;

  while(node) {
    // reverse pointer
    node.next = previous;
    // increment previous to current node
    previous = node;
    // increment node to next node
    if (node.next){
      node = node.next
    } else {
      node = null;
    }
  }
}

Примечание: В моем поиске подобных сообщений я нашел пример in Яваскрипт. Мне было интересно, возможен ли мой код (без temp переменной). Спасибо.

3 ответов


есть несколько проблем с вашим кодом. Это должно прояснить ситуацию.

// reverse a linked list  
var reverseLinkedList = function(linkedlist) {
  var node = linkedlist;
  var previous = null;

  while(node) {
    // save next or you lose it!!!
    var save = node.next;
    // reverse pointer
    node.next = previous;
    // increment previous to current node
    previous = node;
    // increment node to next node or null at end of list
    node = save;
  }
  return previous;   // Change the list head !!!
}
linkedlist = reverseLinkedList(linkedlist);

вы можете решить эту проблему рекурсивно за O (n) время как ckersch упоминает. Дело в том, что вам нужно знать, что рекурсия является интенсивной памятью, так как функции накапливаются в стеке вызовов, пока они не достигнут условия остановки и не начнут возвращать фактические вещи.

Я бы решил эту проблему следующим образом:

 const reverse = (head) => {
   if (!head || !head.next) {
     return head;
   }
   let temp = reverse(head.next);
   head.next.next = head;
   head.next = undefined;
   return temp;
 }    

когда reverse () достигает конца списка, он захватывает последний узел в качестве новой головки и ссылается на каждый узел назад.


это будет O(n) во времени, так как вы выполняете постоянное количество операций на каждом узле. Концептуально, нет более эффективного способа делать вещи (с точки зрения нотации big-O, есть некоторая оптимизация кода, которую можно сделать.)

причина, по которой вы не можете превышать O(n), заключается в том, что для этого вам нужно будет пропустить некоторые узлы. Поскольку вам нужно изменить каждый узел, это было бы невозможно.

эффективность затем сводится к постоянному фактору. Этот чем меньше операций вы можете выполнить для каждого элемента в списке, тем быстрее будет выполняться ваш код.

Я бы реализовал следующее:

function reverseLinkedList(list, previous){

  //We need to use the the current setting of
  //list.next before we change it. We could save it in a temp variable,
  //or, we could call reverseLinkedList recursively
  if(list.next !== null){
    reverseLinkedList(list.next, list);
  }

  //Everything after 'list' is now reversed, so we don't need list.next anymore.
  //We passed previous in as an argument, so we can go ahead and set next to that.
  list.next = previous;
}

reverseLinkedList(list, null);

конечно, это рекурсивно, поэтому было бы неэффективно с точки зрения пространства, но мне нравится рекурсивный код:)

Это также не возвращает обратный связанный список, но мы могли бы довольно легко изменить вещи, чтобы сделать это, если бы это было важно.