【问题标题】:Reversing single linked list in C#在 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 在什么命名空间中?

标签: c# reverse singly-linked-list


【解决方案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&lt;T&gt; 来解决这个问题花了我太长时间:

              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&lt;T&gt;.Next 属性使LinkedList&lt;T&gt; 在这里如此重要。 (鼓励非计算机科学的人研究数据结构的历史——我们应该问一个问题,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&lt;T&gt; { public T Head { get; private set; } public Node&lt;T&gt; Tail { get; private set; } public static Node&lt;T&gt; Empty = new Node&lt;T&gt;(); private Node() {} public Node&lt;T&gt; Push(T t) =&gt; new Node&lt;T&gt; { Head = t, Tail = this } } 现在我们实现一个可变的class List&lt;T&gt; { private Node&lt;T&gt; n = Node&lt;T&gt;.Empty; public bool IsEmpty =&gt; n == Node&lt;T&gt;.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 的那个。

                    【讨论】:

                      猜你喜欢
                      • 2016-03-19
                      • 2012-10-26
                      • 2016-04-22
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-09-29
                      相关资源
                      最近更新 更多