如何向后读取单链表?
我能想到的一种方法是反转列表然后读取它。 但这涉及到更改列表,这是不好的。
或者我可以复制列表,然后反转它,但这会使用额外的 O(n) 内存。 有没有更好的方法,不使用额外的内存,不修改列表并以 O(n) 时间运行
反向链表代码类似于 c# 中的
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);
}
One method which I can think of is to reverse the list and then read it.
But this involves changing the list which is bad.
OR I can make a copy of the list and then reverse it, but this uses additional O(n) memory.
Is there any better method which doesn't use extra memory and doesn't modify the list and runs in O(n) time
reverse linked list code is something like this in c#
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;
}
Recursive solution is
void ReadBackWard (Node n)
{
if (n==null)
return;
else
ReadBackward(n.Next);
Console.WriteLine(n.Data);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(13)
还有第三种解决方案,这次使用
O(log(n))
内存和O(n log(n))
时间,从而占据两者之间的中间立场马克的回答中的解决方案。它实际上是二叉树的逆序下降 [
O(log(n))
],只不过在每一步都需要找到树的顶部 [O(n) )
]:这是Python中的解决方案(我不知道C#):
这可以重新转换使用迭代而不是递归,但以清晰度为代价。
例如,对于一百万个条目的列表,三个解决方案的顺序为:
There is a third solution, this time using
O(log(n))
memory andO(n log(n))
time, thus occupying the middle ground between the two solutions in Marc's answer.It is effectively a reverse in-order descent of a binary tree [
O(log(n))
], except at each step you need to find the top of the tree [O(n)
]:Here is the solution in Python (I don't know C#):
This could be recast using iteration instead of recursion, but at the cost of clarity.
For example, for a million-entry list, the three solutions take on the order of:
假设您的单链表实现了 IEnumerable,您可以利用 LINQ 的反向扩展方法:
您需要在代码文件顶部添加一个
using System.Linq;
指令才能使用 LINQ扩展方法。Assuming your singly-linked list implements IEnumerable<T>, you can utilize LINQ's Reverse extension method:
You'll need to add a
using System.Linq;
directive at the top of the code file to use LINQ's extension methods.创建堆栈并将所有元素推入堆栈的一种变体是使用递归(以及系统内置的堆栈),这可能不是使用生产代码的方法,但可以作为更好的(恕我直言)面试答案原因如下:
A variation of creating a stack and pushing all the elements onto the stack is to use recursion (and the system's built in stack), this is probably not the way to go with production code but serves as a better (IMHO) interview answer for the following reasons:
好吧,天真的解决方案是跟踪您当前所在的节点,然后从头开始迭代,直到找到该节点,始终保存您刚刚离开的节点。 然后,每次找到当前所在的节点时,都会生成刚刚离开的节点,将该节点保存为当前所在的节点,然后从头开始重新迭代。
这当然会导致性能非常糟糕。
我确信一些聪明的人有更好的解决方案。
伪代码(甚至有错误):
Well, the naive solution would be to keep track of which node you're currently at, then iterate from the start until you find that node, always saving the node you just left. Then each time you find the node you're currently at, you produce the node you just left, save that node as the one you're currently at, then re-iterate from the start.
This would of course be horribly bad performance-wise.
I'm sure some smarter people have a better solution.
Pseudo-code (with bugs even):
这很混乱但有效:
}
This is messy but works:
}
如果在显式堆栈程序中,我们只为每个节点的数据创建一个堆栈(而不是创建
类型的堆栈,而是创建类型的堆栈)
)不是更好吗? 因为此时我们不需要存储 Node 的任何其他信息。If in the Explicit Stack program, we create a stack for just the data of each node (instead of creating the Stack of type
<Node>
, we create Stack of type<T>
) wouldn't it be even better? Because we don't need to store any other information of the Node then.你可以在 O(n^2) 内读取它——每次转到最后一个节点读取并打印出前一个节点
you could read it in O(n^2) -- every time go to the last node read and print out the previous one
有什么问题:
O(n) 性能、O(1) 内存并且简单如do-re-mi!
What's wrong with:
O(n) performance, O(1) memory and simple as do-re-mi!
要使用 O(n) 内存和 O(n) 性能,请创建堆栈; 当您向前迭代时,将所有内容推上,然后将所有内容弹出,从而产生结果。
要使用 O(n^2) 性能(但需要 O(1) 额外内存),请每次向前读取它,向上读取最后一个节点之前的节点。
例子:
To use O(n) memory and O(n) performance, create a stack; push everything on as you iterate in the forwards direction, then pop everything off, yielding the results.
To use O(n^2) performance (but O(1) extra memory), read it forwards each time, up the the node before the last one you got to.
Example:
单向链表的特征之一是它实际上是单向链表。 这是一条单向街,没有办法克服它,除非你把它变成其他东西(比如反向单链表、堆栈、双向链表……)。 一个人必须忠于事物的本质。
正如前面所指出的; 如果您需要双向遍历列表; 你需要有一个双向链表。 这就是双向链表的本质,它是双向的。
One of the hallmarks of a singly-linked list is that it is, in fact, singly linked. It is a one-way street, and there's no way to overcome that unless you turn it into something else (such as a reversed singly-linked list, a stack, a doubly-linked list...). One must be true to the nature of things.
As has been pointed out earlier; if you need to traverse a list both ways; you need to have a doubly-linked list. That is the nature of a doubly linked list, it goes both ways.
实际上你应该使用双向链表。
如果这是不可能的,我认为最好的选择是构建已反转列表的副本。
如果列表太长,其他选项(例如依赖递归(有效地将列表复制到堆栈))可能会导致堆栈空间不足。
Really you should be using a doubly-linked list.
If this isn't possible, I think your best option will be to construct a copy of the list that has been reversed.
Other options, such as relying on recursion (effectively copying the list to the stack) could cause you to run out of stack space if the list is too long.
如果内存不足,您可以反转列表,迭代它并再次反转。 或者,您可以创建一个指向节点的指针堆栈(或者类似于 C# 中的指针的任何内容)。
If you short of memory you can reverse list, iterate over it and reverse it again. Alternatively you can make a stack of pointers to nodes (or whatever is like a pointer in C#).