【问题标题】:C# singly linked list implementationC#单链表实现
【发布时间】:2013-12-03 22:18:17
【问题描述】:

在尝试了解如何在 C# 中实现单个列表时,我遇到了以下链接:

Creating a very simple linked list.

但是,由于我是 C# 新手,所以我对上面讨论的初始部分中列出的语法感到困惑。正在声明一个名为 Node 的类,并且该类中有另一个声明为“public Node next”。该语句是否称为构造函数?请帮忙。

public class Node {
    public Node next;
    public Object data;
 }

【问题讨论】:

    标签: c# .net list constructor linked-list


    【解决方案1】:

    在一个简单的单链表实现中,Node 类型包含对列表中下一项的引用,这就是您发布的Node 类型中的next 字段所做的。此引用用于允许列表的迭代。

    封闭的LinkedList 类(或任何你想叫它的)将包含一个Node 对列表中第一项的引用。从第一个节点开始,您可以通过获取next 字段来逐步浏览列表。当next 为空时,您已到达列表的末尾。

    以这段代码为例:

    public class LinkedList
    {
        public class Node
        {
            // link to next Node in list
            public Node next = null;
            // value of this Node
            public object data;
        }
    
        private Node root = null;
    
        public Node First { get { return root; } }
    
        public Node Last 
        {
            get
            {
                Node curr = root;
                if (curr == null)
                    return null;
                while (curr.next != null)
                    curr = curr.next;
                return curr;
            }
        }
    }
    

    First 属性只返回根节点,即列表中的第一个节点。 Last 属性从根节点开始遍历列表,直到找到next 属性为空的节点,表示列表结束。

    这使得将项目附加到列表变得简单:

    public void Append(object value)
    {
        Node n = new Node { data = value };
        if (root == null)
            root = n;
        else
            Last.next = n;
    }
    

    要删除一个节点,您必须在列表中找到它之前的节点,然后从 那个 节点更新next 链接以指向要删除的节点之后的节点:

    public void Delete(Node n)
    {
        if (root == node) 
        {
            root = n.next;
            n.next = null;
        }
        else
        {
            Node curr = root;
            while (curr.next != null)
            {
                if (curr.next == n)
                {
                    curr.next = n.next;
                    n.next = null;
                    break;
                }
                curr = curr.next;
            }
        }
    }
    

    您还可以执行一些其他操作,例如在列表中的位置插入值、交换节点等。在节点之后插入很快,在之前插入很慢,因为您必须找到前一个节点。如果您真的想要快速“插入之前”,您需要使用双向链表,其中 Node 类型同时具有 nextprevious 链接。


    在评论中扩展您的问题...

    在 C# 中,所有类型都分为两种基本分类:值类型和引用类型。名称反映了它们在代码块之间传递的方式:值类型通过值传递(值被复制到新变量),而引用类型通过引用传递(引用/指针被复制到新变量)。不同之处在于对值类型参数的更改不会影响调用者的值副本,而对引用类型参数的更改将反映在调用者的引用副本中。

    赋值和引用变量也是如此。如下,b 变化时a 的值不变:

    int a = 0;
    int b = a;
    b = 1;
    

    这相当直观。可能会让您感到困惑的是,在 C# 中,struct 也是一个值类型:

    public struct test
    {
        public string value;
    }
    
    static void Main()
    {
        test a;
        a.value = "a";
        test b = a;
        b.value = "b";
    
        Console.WriteLine("{0} {1}", a.value, b.value);
    }
    

    上面将给出输出a b,因为当您将a 分配给b 时,复制了一份。但是如果我们改变一个类的结构:

    public class test
    {
        public string value;
    }
    
    static void Main()
    {
        test a = new test(); // Note the 'new' keyword to create a reference type instance
        a.value = "a";
        test b = a;
        b.value = "b";
        Console.WriteLine("{0} {1}", a.value, b.value);
    }
    

    因为变量b 是对相同 对象的引用,因为它与a 引用的一个变量相同,所以这里的输出将是b b。这两个变量引用同一个对象

    如果您来自 C/C++ 或其他类似语言,您可以将引用类型变量视为指针。它并不完全相同,C# 实际上确实有指针(它们对正常的托管代码是隐藏的),但它已经足够接近了。在您将其指向该类型的 instance 之前,它并不完全可用。就像 C/C++ 中的 char* 在您将其指向某个地方之前并不是特别有用。

    Joseph Alhabari(写了一篇关于值和引用类型的精彩文章:C# Concepts: Value vs Reference Types。值得一读,就像他所写的大部分内容一样。我也强烈建议您考虑获取他的一篇C# Nutshell书。

    【讨论】:

    • 节点 curr = root;抱歉,如果这是一个愚蠢的问题,但我只是想在 C# 方面做得更好,不想做任何假设。当您做出此声明时,我是否正确理解您正在实例化 Node 类型的对象?如果是这样,让我困惑的是内存空间将如何分配,因为执行“Node curr”时内部还有另一个“public Node next”声明?
    • 不,声明是一个变量,可以持有一个引用,在概念上类似于C或C++中的指针。所有类变量都是如此。要创建类对象的实例,您需要使用 new 关键字。
    【解决方案2】:

    有一种创建单链表的简单方法。让我们试着理解这个概念。如果概念清楚,那么您可以理解逻辑本身。单链表有两个部分的节点。一个有数据值,另一个有下一个节点的参考地址。看看下面的代码:

    首先我们需要创建链表节点类

    /// <summary>
    /// Creating the Real World Entity of Linked List Node
    /// </summary>
    public class LinkedListNode
    {
        public Object Value { get; set; }
    
        public LinkedListNode Next { get; set; }
    
    }
    

    这里的类有 Value 和 Holder 来保存序列中下一个 Node 的引用。接下来我们需要自己创建链表

    /// <summary>
    /// Creating the Linked List Class Itself. It defines the First and Last Nodes of Linked List
    /// </summary>
    public class LinkedList
    {
        public LinkedListNode First { get; set; }
        public LinkedListNode Last { get; set; }
    
        /// <summary>
        /// Method to Add items into the Linked List
        /// </summary>
        /// <param name="_value"></param>
        public void AddToLinkedList(object _value)
        {
            LinkedListNode node = new LinkedListNode();
            node.Value = _value;
    
            if (First == null)
            {
                First = node;
                Last = node;
            }
            else
            {
                Last.Next = node;
                Last = node;
            }
        }
    
        /// <summary>
        /// Method to display all items. We can further implement the IEnumerable interface
        /// to Yield IEnumerator Interface.
        /// </summary>
        public void DisplayAllItems()
        {
            LinkedListNode current = First;
            while (current != null)
            {
                Console.WriteLine(current.Value);
                current = current.Next;
            }
        }
    }
    

    这里的关键是将项目添加到链接列表中。首先我们需要检查链表是否存在。我们检查链表中的第一个或头节点。如果它是空的,我们将节点分配为第一个入口点。在这个阶段,最后一项就是第一项本身。

    现在这就是我们添加和显示项目的方式

    class Program
    {
        static void Main(string[] args)
        {
            LinkedList singlyLinkedList = new LinkedList();
            singlyLinkedList.AddToLinkedList(4);
            singlyLinkedList.AddToLinkedList(5);
            singlyLinkedList.AddToLinkedList(7);
            singlyLinkedList.AddToLinkedList(2);
            singlyLinkedList.AddToLinkedList(1);
            singlyLinkedList.AddToLinkedList(10);
    
            singlyLinkedList.DisplayAllItems();
            Console.ReadLine();
        }
    }
    

    让我知道这是否有意义:)

    【讨论】:

      【解决方案3】:

      【讨论】:

        【解决方案4】:

        请记住,Linked-List 不仅包含数据,还包含指向列表中下一个节点的引用/指针。由于类是 C# 中的引用类型,因此您看不到任何在 C 或 C++ 中会看到的特殊语法。

        struct Node
        {
            int Number;
            struct Node* next; /* this points to the next node in the linked list */
        };
        

        【讨论】:

        • 感谢大家的帮助。看了微软网站上的字段描述,我的理解更加清晰了。但是,只有一个问题:如果字段声明与类同名,是否有一些术语来定义这种类型的字段?
        • @user3011489 我确定有...因为似乎所有事物都有一个术语...但我不知道或无法想到它我的头。我认为,当使用从开发人员那里抽象出很多东西的高级语言时,实现对链表、双向链表等问题的解决方案会变得更加复杂。
        • 感谢您的帮助。
        猜你喜欢
        • 2015-08-18
        • 1970-01-01
        • 1970-01-01
        • 2015-06-03
        • 1970-01-01
        • 1970-01-01
        • 2011-09-17
        • 2019-09-02
        • 2021-11-13
        相关资源
        最近更新 更多