Как читать отдельно связанный список назад?

Один из способов, о котором я могу думать, - обратить вспять список, а затем прочитать его. Но это связано с изменением списка, который является плохим.
ИЛИ Я могу сделать копию списка, а затем отменить его, но это использует дополнительную память O (n). Есть ли лучший способ, который не использует дополнительную память и не изменяет список и не запускается в O (n) time

код с обратным соединением выглядит примерно так: С#

Void Reverse (Node head)
{
    Node prev= null;
    Node current = head;
    Node nextNode = null;

        while (current!=null)
        {
            nextNode = current.Next;
            current.Next = prev;
            prev=current;
            current = nextNode; 

        }
        head = prev;

}   

Рекурсивное решение

void ReadBackWard (Node n)
{
    if (n==null)
        return;
    else
        ReadBackward(n.Next);

    Console.WriteLine(n.Data);

}

Ответ 1

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

Чтобы использовать O (n ^ 2) производительность (но O (1) дополнительную память), читайте его вперед каждый раз, до node до последнего, до которого вы добрались.

Пример:

IEnumerable<T> Reverse (Node head) {
    Stack<Node> nodes = new Stack<Node>();
    while(head != null) {
        nodes.Push(head);
        head = head.Next;
    }
    while(nodes.Count > 0) {
        yield return nodes.Pop().Value;
    }
}

Ответ 2

Одна из отличительных черт одноуровневого списка состоит в том, что он, по сути, связан с одинокой. Это улица с односторонним движением, и нет способа ее преодолеть, если вы не превратите ее во что-то другое (например, обратный односвязный список, стек, двусвязный список...). Нужно быть верным природе вещей.

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

Ответ 3

Действительно, вы должны использовать двусвязный список.

Если это невозможно, я думаю, что ваш лучший вариант - создать копию списка, который был отменен.

Другие параметры, такие как использование рекурсии (эффективное копирование списка в стек), могут привести к выходу из пространства стека, если список слишком длинный.

Ответ 4

Если вам не хватает памяти, вы можете переименовать список, перебрать его и снова отменить. В качестве альтернативы вы можете сделать стек указателей на узлы (или что-то вроде указателя на С#).

Ответ 5

void reverse_print(node *head) 
{
    node *newHead = NULL, *cur = head;

    if(!head) return;

    // Reverse the link list O(n) time O(1) space
    while(cur){
        head = head->next;
        cur->next = newHead;
        newHead = cur;
        cur = head;
    }

    // Print the list O(n) time O(1) space
    cur = newHead;
    while(cur) {
        printf(" %d", cur->val);
        cur = cur->next;
    }

    // Reverse the link list again O(n) time O(1) space
    cur = newHead;
    while(cur){
        newHead = newHead->next;
        cur->next = head;
        head = cur;
        cur = newHead;
    }
    // Total complexity O(n) time O(1) space
}

Ответ 6

Существует третье решение, на этот раз использующее O(log(n)) память и время O(n log(n)), тем самым занимая промежуточную точку между двумя решениями в ответе Марка.

Это фактически обратное схождение двоичного дерева в обратном порядке [ O(log(n))], за исключением того, что на каждом шаге вам нужно найти вершину дерева [O(n)]:

  • Разделить список двумя
  • Запишите во вторую половину списка
  • Распечатайте значение в средней точке
  • Считайте в первом тайме

Вот решение в Python (я не знаю С#):

def findMidpoint(head, tail):
  pos, mid = head, head
  while pos is not tail and pos.next is not tail:
    pos, mid = pos.next.next, mid.next
  return mid

def printReversed(head, tail=None):
  if head is not tail:
    mid = findMidpoint(head, tail)
    printReversed(mid.next, tail)
    print mid.value,
    printReversed(head, mid)

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

Например, для списка из миллиона записей три решения принимают порядок:

Solution   Memory       Performance
=========================================
 Marc #1     4MB    1 million operations
  Mine       80B    20 million operations
 Marc #2      4B    1 trillion operations

Ответ 7

Предполагая, что ваш односвязный список реализует IEnumerable <T> , вы можете использовать метод расширения LINQ Reverse:

var backwards = singlyLinkedList.Reverse();

Вам нужно добавить директиву using System.Linq; в верхней части файла кода, чтобы использовать методы расширения LINQ.

Ответ 8

Вариант создания стека и толкание всех элементов в стеке - это использование рекурсии (и системы, построенной в стеке), это, вероятно, не способ пойти с производственным кодом, но служит лучшим (IMHO) интервью ответьте на следующие причины:

  • Это показывает, что вы рекурсии grok
  • Это меньше кода и выглядит более элегантно
  • Наивный интервьюер может не понимать, что накладные расходы на место (если это так, вы можете подумать, хотите ли вы там работать).

Ответ 9

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

Это, конечно, было бы ужасно плохой по производительности.

Я уверен, что у более умных людей есть лучшее решение.

Псевдокод (с ошибками даже):

current node = nothing
while current node is not first node
    node = start
    while node is not current node
        previous node = node
        node = next node
    produce previous node
    set current node to previous node

Ответ 10

Это грязно, но работает:

class SinglyLinkedList {
SinglyLinkedList next;
int pos;
SinglyLinkedList(int pos) {
    this.pos = pos;
}
SinglyLinkedList previous(SinglyLinkedList startNode) {
    if (startNode == this) return null;
    if (startNode.next == this) return startNode;
    else return previous(startNode.next);
}

static int count = 0;
static SinglyLinkedList list;
static SinglyLinkedList head;
static SinglyLinkedList tail;
public static void main (String [] args) {
    init();

    System.out.println("Head: " + head.pos);
    System.out.println("Tail: " + tail.pos);

    list = head;
    System.out.print("List forwards: ");
    while (list != null) {
        System.out.print(list.pos + ",");
        list = list.next;
    }

    list = tail;
    System.out.print("\nList backwards: ");
    while (list.previous(head) != null) {
        System.out.print(list.pos + ",");
        list = list.previous(head);
    }
}
static void init() {
    list = new SinglyLinkedList(0);
    head = list;
    while (count < 100) {
        list.next = new SinglyLinkedList(++count);
        list = list.next;
    }
    tail = list;
}

}

Ответ 11

Если в программе Explicit Stack мы создаем стек только для данных каждого node (вместо создания Stack типа <Node>, мы создаем Stack типа <T>), не было бы даже лучше? Потому что нам не нужно хранить какую-либо другую информацию из node.

IEnumerable<T> Reverse (Node<T> head) {
    Stack<T> nodes = new Stack<T>();
    while(head != null) {
        nodes.Push(head.data);
        head = head.Next;
    }
    while(nodes.Count > 0) {
        yield return nodes.Pop();
    }
}

Ответ 12

вы могли бы прочитать его в O (n ^ 2) - каждый раз переходите к последнему node, чтобы прочитать и распечатать предыдущий

Ответ 13

Что случилось с:

    public void printBackwards(LinkedList sl){    
        ListIterator<Element> litr = sl.listIterator(sl.size());
        Element temp;
        while(litr.previousIndex() >= 0){
            temp = litr.previous();
            System.out.println(temp);
        }
    }

O (n), O (1) память и просто, как do-re-mi!