• 泛型系列<5>:链表的实现


    4.6 链表的实现

    问题

    您需要链表数据结构,这样就可以很容易地添加和删除元素。

    解决方案

    使用泛型LinkedList<T>类。下面的方法创建了一个LinkedList<T>类,并往链表对象中添加节点,然后使用了几种方法从链表节点中获得信息。

        public static void UseLinkedList()

        {

            // 创建一个LinkedList 对象.

            LinkedList<TodoItem> todoList = new LinkedList<TodoItem>();

            // 创建添加到链表内的TodoItem对象.

            TodoItem i1 = new TodoItem("paint door", "Should be done third");

            TodoItem i2 = new TodoItem("buy door", "Should be done first");

            TodoItem i3 = new TodoItem("assemble door", "Should be done second");

            TodoItem i4 = new TodoItem("hang door", "Should be done last");

            // 添加项目.

            todoList.AddFirst(i1);

            todoList.AddFirst(i2);

            todoList.AddBefore(todoList.Find(i1), i3);

            todoList.AddAfter(todoList.Find(i1), i4);

            // 显示所有项目.

            foreach (TodoItem tdi in todoList)

            {

                Console.WriteLine(tdi.Name + " : " + tdi.Comment);

            }

            // 显示链表内的第二个节点的信息

            Console.WriteLine("todoList.First.Next.Value.Name == " +

                                todoList.First.Next.Value.Name);

            // 显示链表内最后一个节点的前一节点信息.

            Console.WriteLine("todoList.Last.Previous.Value.Name == " +

                                todoList.Last.Previous.Value.Name);

    }

    这个方法的输出结果如下:

        buy door : Should be done first

        assemble door : Should be done second

        paint door : Should be done third

        hang door : Should be done last

        todoList.First.Value.Name == buy door

        todoList.First.Next.Value.Name == assemble door

        todoList.Last.Previous.Value.Name == paint door

     

    下面是TodoItem类,它只简单地包含了两个字符串_name_comment

    public class TodoItem

    {

        public TodoItem(string name, string comment)

        {

            _name = name;

            _comment = comment;

        }

        private string _name = "";

        private string _comment = "";

        public string Name

        {

            get { return (_name); }

            set { _name = value; }

        }

        public string Comment

        {

            get { return (_comment); }

            set { _comment = value; }

        }

    }

    讨论

    LinkedList<T>类在.NET framework中是一个双向链表。这是因为链表中的每一个节点都包含了前一节点和后一节点的指针。图4-1演示了这种结构,图中的每个node代表了一个单独的LinkedListNode<T>对象。

    注意图中链表的每个节点(方块)包含了一个指向下一节点的指针(指向右边的箭头)和一个指向前一节点的指针(指向左边的箭头)。相反,单链表只包含指向下一节点的指针,它没有指向前一节点的指针。

    LinkedList类中,前一节点通过访问Previous属性获得,后一节点通过访问Next属性获得。链表中的第一个节点的Previous属性总是返回null值。两样,最后一个节点的Next属性也是返回null值。

    链表中的每个节点(图4-1中用方块表示)实际上都是一个LinkedListNode<T>对象。所以LinkedList<T>对象实际上是由一组LinkedListNode<T>对象组成,所有这些LinkedListNode<T>对象都包含了访问下一个和前一个LinkedListNode<T>对象的属性。LinkedListNode<T>中所包含的对象可以通过Value属性访问。除了这些属性外,LinkedListNode<T>对象还有一个属性叫List,可以用它来访问所属的LinkedList<T>对象。

    性能是我们非常关心的一个问题,List<T>类的性能优越于LinkedList<T>类。一般情况下在List<T>内添加和删除节点要比在LinkedList<T>内进行同样的操作快。对比List<T>.Add方法和LinkedList<T>类的Add*方法(译者注:之所以是Add*方法是因为LinkedList<T>中的添加方法有:AddAfterAddBeforeAddFirstAddLast),导到性能上的差异并非因为添加操作本身,而是LinkedList<T>在垃圾回收时的压力。List<T>的数据本质上是存放在托管堆中的一个大容量数组之上,然而LinkedList<T>有可能会把它的节点存放在托管堆的每个角落。这使得强制垃圾回收在处理托管堆中的LinkedList<T>节点对象时需要花费更多的力气。需要注意,List<T>.Insert方法可能会比LinkedList<T>中的任一个Add*方法要慢,但这取决于对象在List<T>的哪个位置插入。当在某个点插入一人新元素时,Insert方法必须移动它后面的所有元素一个位置。如果新元素插入到List<T>的尾部,移动元素所花费的开销比起垃圾回收所花费的开销就可以忽略不计了。

    List<T>另外一个胜于LinkedList<T>的地方是可以使用索引访问。在List<T>中,您可以使用索引器并通过索引值来访问指定位置的某个元素。但在LinkedList<T>中就没有这么令人愉快了。在LinkedList<T>类中,您必须使用每个LinkedListNode<T>中的PreviousNext属性进行导航,并贯穿整个链表直到找到您所指定的位置。

    在搜索一个元素或节点时,List<T>类也比LinkedList<T>类有速度上的优势。使用List<T>.BinarySearch方法在List<T>内查找元素比在LinkedList<T>类中使用相应的ContainsFindFindLast方法更快,这是因为LinkedList<T>的方法执行线性搜索而List<T>.BinarySearch方法执行二分查找法。一般条件下,二分查找法利用元素在List<T>内是按顺序排列的。这使得在进行二分查找之前必须调用Sort方法(注意:当添加新元素时,Sort方法也会在BinarySearch方法之前被调用)。利用这些,二分查找将检查列表中的中间那个元素,并询问:你查找的对象是否大于列表中的当前对象?如果是这样,可知目标对象索引值将在当前对象之前。如果不是,则对象索引值在当前索引之后。二分查找算法保持询问,直到找到对象为止。恰恰相反,线性搜索从列表中的第一个元素开始查找指定元素,如果不是,则继续搜索下一个元素,直到在列表中找到相应的元素。

  • 相关阅读:
    机器码call和jmp地址的计算
    linux下系统对于sigsegv错误时的处理
    elf文件中的.plt .rel.dyn .rel.plt .got .got.plt的关系
    docker随谈
    nginx性能优化技巧
    ubuntu安装php常见错误集锦
    ubuntu php5.6源码安装
    关于mysqld_safe
    ubuntu mysql5.7源码安装
    linux下nginx模块开发入门
  • 原文地址:https://www.cnblogs.com/kakaliush/p/2271359.html
Copyright © 2020-2023  润新知