在 C# 中反转单链表
Posted
技术标签:
【中文标题】在 C# 中反转单链表【英文标题】:Reversing single linked list in C# 【发布时间】:2012-01-30 22:51:05 【问题描述】:我正在尝试反转链接列表。这是我想出的代码:
public static void Reverse(ref Node root)
Node tmp = root;
Node nroot = null;
Node prev = null;
while (tmp != null)
//Make a new node and copy tmp
nroot = new Node();
nroot.data = tmp.data;
nroot.next = prev;
prev = nroot;
tmp = tmp.next;
root = nroot;
它运行良好。想知道是否可以避免创建新节点。想对此提出建议。
【问题讨论】:
为什么要为此实现自定义集合?System.Collections
命名空间中可用的选项都不能满足您的要求吗?
我正在学习并准备面试。
Node 在什么命名空间中?
【参考方案1】:
public class Node<T>
public T Value get; set;
public Node<T> Next get; set;
public static Node<T> Reverse<T>(Node<T> head)
Node<T> tail = null;
while(head!=null)
var node = new Node<T> Value = head.Value, Next = tail ;
tail = node;
head = head.Next;
return tail;
【讨论】:
【参考方案2】:这在 Leetcode 上表现不错。
public ListNode ReverseList(ListNode head)
ListNode previous = null;
ListNode current = head;
while(current != null)
ListNode nextTemp = current.next;
current.next = previous;
previous = current;
current = nextTemp;
return previous;
【讨论】:
【参考方案3】:如果您想要一个现成的高效实现,我创建了 LinkedList 的替代方案,它支持枚举和反向操作。 https://github.com/NetFabric/NetFabric.DoubleLinkedList
【讨论】:
【参考方案4】:链表逆递归
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ReverseLinkedList
class Program
static void Main(string[] args)
Node head = null;
LinkedList.Append(ref head, 25);
LinkedList.Append(ref head, 5);
LinkedList.Append(ref head, 18);
LinkedList.Append(ref head, 7);
Console.WriteLine("Linked list:");
LinkedList.Print(head);
Console.WriteLine();
Console.WriteLine("Reversed Linked list:");
LinkedList.Reverse(ref head);
LinkedList.Print(head);
Console.WriteLine();
Console.WriteLine("Reverse of Reversed Linked list:");
LinkedList.ReverseUsingRecursion(head);
head = LinkedList.newHead;
LinkedList.PrintRecursive(head);
public static class LinkedList
public static void Append(ref Node head, int data)
if (head != null)
Node current = head;
while (current.Next != null)
current = current.Next;
current.Next = new Node();
current.Next.Data = data;
else
head = new Node();
head.Data = data;
public static void Print(Node head)
if (head == null) return;
Node current = head;
do
Console.Write("0 ", current.Data);
current = current.Next;
while (current != null);
public static void PrintRecursive(Node head)
if (head == null)
Console.WriteLine();
return;
Console.Write("0 ", head.Data);
PrintRecursive(head.Next);
public static void Reverse(ref Node head)
if (head == null) return;
Node prev = null, current = head, next = null;
while (current.Next != null)
next = current.Next;
current.Next = prev;
prev = current;
current = next;
current.Next = prev;
head = current;
public static Node newHead;
public static void ReverseUsingRecursion(Node head)
if (head == null) return;
if (head.Next == null)
newHead = head;
return;
ReverseUsingRecursion(head.Next);
head.Next.Next = head;
head.Next = null;
public class Node
public int Data = 0;
public Node Next = null;
【讨论】:
【参考方案5】:复杂度 O(n+m)。假设 head 是起始节点:
List<Node>Nodes = new List<Node>();
Node traverse= root;
while(traverse!=null)
Nodes.Add(traverse);
traverse = traverse.Next;
int i = Nodes.Count - 1;
root = Nodes[i];
for(; i>0; i--)
Nodes[i].Next = Nodes[i-1];
Nodes[0].Next=null;
【讨论】:
【参考方案6】:这里是反转链表的示例代码。
使用系统;
class Program
static void Main(string[] args)
LinkItem item = generateLinkList(5);
printLinkList(item);
Console.WriteLine("Reversing the list ...");
LinkItem newItem = reverseLinkList(item);
printLinkList(newItem);
Console.ReadLine();
static public LinkItem generateLinkList(int total)
LinkItem item = new LinkItem();
for (int number = total; number >=1; number--)
item = new LinkItem
name = string.Format("I am the link item number 0.", number),
next = (number == total) ? null : item
;
return item;
static public void printLinkList(LinkItem item)
while (item != null)
Console.WriteLine(item.name);
item = item.next;
static public LinkItem reverseLinkList(LinkItem item)
LinkItem newItem = new LinkItem
name = item.name,
next = null
;
while (item.next != null)
newItem = new LinkItem
name = item.next.name,
next = newItem
;
item = item.next;
return newItem;
class LinkItem
public string name;
public LinkItem next;
【讨论】:
【参考方案7】:几年前,我错过了一个时髦的洛杉矶娱乐公司 ASP.NET MVC 开发人员职位,因为我无法回答这个问题 :((这是一种淘汰非计算机科学专业的方法。)所以我尴尬地承认,我在 LINQpad 中使用实际的 LinkedList<T>
来解决这个问题花了我太长时间:
var linkedList = new LinkedList<int>(new[]1,2,3,4,5,6,7,8,9,10);
linkedList.Dump("initial state");
var head = linkedList.First;
while (head.Next != null)
var next = head.Next;
linkedList.Remove(next);
linkedList.AddFirst(next.Value);
linkedList.Dump("final state");
只读的LinkedListNode<T>.Next
属性使LinkedList<T>
在这里如此重要。 (鼓励非计算机科学的人研究数据结构的历史——我们应该问一个问题,linked list 来自哪里——它为什么存在? )
【讨论】:
和你一样,上周我错过了一个 ASP.NET MVC 开发人员职位,因为同样的问题是关于反转链表:( 好一个!尽管反转链表是一个普遍的问题,但与 C++ 或 Java 相比,实现它 C# 是一个不同且棘手的问题,因为 C# 的节点不允许设置 next。因此,您必须考虑下一步的设置并考虑删除!【参考方案8】:public Node ReverseList(Node cur, Node prev)
if (cur == null) // if list is null
return cur;
Node n = cur.NextNode;
cur.NextNode = prev;
return (n == null) ? cur : ReverseList(n, cur);
【讨论】:
cur 似乎是根 - prev 是什么?【参考方案9】:ref 的定义是不必要的,因为如果你把节点设为引用类型,这样做是可以的:
public static void Reverse(Node root)
另外,面试问题的美妙之处在于更少的记忆消耗和到位的反转。也许还要求使用递归方式。
【讨论】:
【参考方案10】:这个问题被问了很多。多年前,当我在采访中被问到这个问题时,我的推理如下:单链表本质上是一个堆栈。因此,反转链表在堆栈上是一项微不足道的操作:
newList = emptyList;
while(!oldList.IsEmpty())
newList.Push(oldList.Pop());
现在您所要做的就是实现 IsEmpty 和 Push 和 Pop,它们是一两行顶部。
我在大约 20 秒内把它写出来,当时面试官似乎有些困惑。我想他希望我花大约 20 分钟来完成大约 20 秒的工作,这对我来说总是很奇怪。
【讨论】:
@AdamRackis:它在语法上也是完美的 javascript 和 C++,可能还有其他一些语言。 :) @Markus:首先,我对此表示怀疑。其中一些解决方案很长。每个堆栈操作都是几行。而对于这个完全微不足道的成本,你会得到一个堆栈 ADT。 根据对抽象数据类型的操作而不是指针和变量来解释您的代码,您的代码将变得更容易理解。根据代码高尔夫来判断候选人的面试官正在寻找错误的信号。 @Markus:在这里,让我们将其放入评论中。我们将首先实现一个不可变的链表:class Node<T> public T Head get; private set; public Node<T> Tail get; private set; public static Node<T> Empty = new Node<T>(); private Node() public Node<T> Push(T t) => new Node<T> Head = t, Tail = this
现在我们实现一个可变的class List<T> private Node<T> n = Node<T>.Empty; public bool IsEmpty => n == Node<T>.Empty; public void Push(T t) n = n.Push(t); public T Pop() T h = n.Head; n = n.Tail; return h;
,我们就完成了。
@Markus:现在我们有了可变堆栈和不可变堆栈。每个方法都在一到三行代码之间,我们可以根据抽象操作来推断列表的行为,而不是根据它是如何使用引用和属性实现的。这不仅比此处发布的任何其他解决方案功能更全面,而且比大多数解决方案短,因为显然简洁是您关心的事情。
现在,当然,在一个适当的实现中,我们将成为好公民并在弹出一个空堆栈时抛出,我们将在不可变堆栈上实现IsEmpty
,以及许多其他可以制作此代码的功能更长。但是使任何代码健壮且功能齐全会使其更长。我在这里的意图不是打高尔夫球最短可能的稳健解决方案。需要指出的是,当您将编码提高一个语义级别时,代码会变得更短且更容易理解它的重要性。【参考方案11】:
您不需要复制。一些伪代码:
prev = null;
current = head;
next = current->next;
(while next != null)
current->next=prev
prev=current
current=next
next=current->next
【讨论】:
【参考方案12】:Node p = root, n = null;
while (p != null)
Node tmp = p.next;
p.next = n;
n = p;
p = tmp;
root = n;
【讨论】:
【参考方案13】:为什么不把 head 指向 tail,tail 指向 head,然后反转 prev 指向的方向遍历列表?
如果您不使用 head 和 tail,只需遍历列表,反转 prev 关系,然后将 head 指向具有 null prev 的那个。
【讨论】:
以上是关于在 C# 中反转单链表的主要内容,如果未能解决你的问题,请参考以下文章