Как читать отдельно связанный список назад?
Один из способов, о котором я могу думать, - обратить вспять список, а затем прочитать его.
Но это связано с изменением списка, который является плохим.
ИЛИ Я могу сделать копию списка, а затем отменить его, но это использует дополнительную память 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!