Henry手记—.NET数据结构对象补遗之单链表(一)

原创 2003年06月16日 09:27:00
  Henry手记—.NET数据结构对象补遗之单链表(一)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

             韩睿  ( 06/15/2003)

 

.NET Framework提供了众多常用的数据结构对象,放在System.Collections命名空间中。现有ArraylistQueueStackSortListHashTable等。但是奇怪的是,微软没有在Framework中加入链表、二叉搜索树等相当重要的数据结构对象。从本篇开始,我们将陆续将它们补充进我们自己的.NET类库,让它们更有效地成为工作的利器。同时,大家也会通过这些基本数据结构的实现,达到对Framework类与接口编程的深入理解,所以,不要错过。同时,我们也一起复习一下数据结构的内容吧。

Arraylist是近来被大家不断提及与关注的列表类,它是由数组构成的。虽然microsoft对数组作了大量的改进,提高了增减元素操作的效率、实现了动态扩充的性能。但是数组始终是数组:在对元素的排序、删除、添加操作时,效率仍很低;更重要的是,数组的大小是固定的,即使是动态数组,只不过是加入了重新分配空间的能力;同时,为了避免频繁地重新分配,不得不将数组初始化大小设得较大,这样就会大量地浪费内存。而事实上,FrameworkQueueStackHashtable等数据集合的实现,也部分或全部地使用了链表的方法,所以,自己动手实践一次,是分外有益的,Let’s go!

1.        单链表(singly linked list)

单链表是最简单的链表表示,因此我们先来对它进行分析与实现。用它来表示线性表时,每一个数据元素占用一个结点(node)。一个结点一般由两个域组成,一个域存放数据元素data;另一个域存放一个指向链表中下一个结点的指针link,它指出下一个结点的开始存储地址。

单链表的类实现,在每一本《数据结构》课本上都有详细的介绍:需要定义结点类和链表类,在链表类中实现对结点的操作。

在本文所要分析的链表类,我们采用嵌套定义的方式,将结点类定义在链表类的内部。所能进行的操作包括:添加(向链表尾插入结点)、插入(向链表中指定的索引位置插入结点)、按索引位置删除结点、按数据元素值删除结点、搜索数据元素值的索引、用For Each遍历链表中的值等。在我们的程序中,还会向大家展示如何在类中设计抛出异常、嵌套枚举器类等不常被提及的处理。

为了避免发生在链表头部或尾部进行插入或删除操作这样特殊的情况,给链表加一个数据为空的头节点。并加入尾结点指针,以方便添加功能的实现。

2.        ArrayList类分析

我们的单链表实现方法是模仿Framework现有的数据集合类的做法,而不是仅仅按照《数据结构》课本的C++定义方法来实现。因此需要先来学习一下Arraylist类的定义:

Public Class ArrayList                

   Implements IList, ICollection, IEnumerable, ICloneable 

也就是说ArrayList是实现了四个接口,我们当然要分别看一下这些接口的作用才好进行仿制工作。

1)IEnumerableIEnumerable是公开枚举数,该枚举数支持在集合上进行简单迭代。必须对它进行实现才能支持 Microsoft Visual Basic ForEach 语义。

2) ICollection:派生自IEnumerable接口,定义所有集合的大小、枚举数和同步方法。ICollection 接口是 System.Collections 命名空间中类的基接口。

3) IListIlist派生自ICollectionIDictionary IList 是基于 ICollection 接口的更专用的接口。IDictionary 实现是键/值对的集合,如 Hashtable 类。IList 实现是可被排序且可按照索引访问其成员的值的集合,如 ArrayList 类。

另:某些集合(如 Queue 类和 Stack 类)限制对其成员的访问,它们直接实现 ICollection 接口。或者当 IDictionary 接口和 IList 接口都不能满足所需集合的要求时,则从 ICollection 接口派生新集合类以提高灵活性。

以上三个接口的继承关系为:IEnumerable-> ICollection-> IList。也就是说ArrayList其实是通过对IList的实现,同时实现了ICollectionIEnumerable。因此我们只需要实现IList接口即可。

4)ICloneable:这个接口的作用就是实现克隆,即用与现有实例相同的值创建类的新实例。为纯粹我们的目标,这个接口不在本文中进行实现。留待后续文章中进行分析。

通过以上的分析,我们可以这样来定义我们即将要实现的单链表类了:

Public Class SLList    

   Implements IList   

要实现接口,即要实现接口中定义的方法或属性。IList接口的实现需要声明的方法与属性如图1所示,一共有15个。分属于IEnumerableICollectionIList

  

   1 单链表类SLList需要实现的来自于接口的方法与属性

3.        实际演练

由于单链表类要能够在用户扩展时当成基类使用,所以它所有的字段、方法和属性是Public或者是Protected的,并且在必要的地方用Overridable进行标记。

接下来,我们依序从类定义到功能实现一一来详细说明:

3.1 类定义

我们用嵌套类定义的方式来定义结点类与链表类:

Imports System.Collections

Public Class SLList

    Implements IList

    Protected Class ListNode

        Public NextNode As ListNode '指向下一个结点的引用

        Public Data As Object '用object类型来声明数据,可以达到template的效果

        Public Sub New(ByVal data As Object, _

                        ByVal nextNode As ListNode)

            '初始化结点

            Me.Data = data

            Me.NextNode = nextNode

        End Sub

        Public Sub New(ByVal data As Object)

            '作为尾结点的结点

            Me.New(data, Nothing)

        End Sub

    End Class

……

3.2 类内公用变量初始化

Protected head As ListNode = New ListNode(Nothing) '初始化头结点

Protected tail As ListNode = head  '指向链表尾的引用

Protected nodeCount As Integer = 0 '保存节点数目,通过Count方法返回

Protected version As Integer = 0 '记录链表变化的版本号

这里使用了version这个变量,它的作用在后面我们会详细讨论的。

3.3验证索引与数据元素

在类里定义了验证函数,验证失败,会抛出异常,这样用户在调用时可以用TryEnd Try来处理:

Protected Overridable Overloads Sub Validate(ByVal index As Integer)

        '验证index是否在可用范围内

        If index < 0 Or index >= nodeCount Then

            Throw New ArgumentOutOfRangeException("索引越界.")

        End If

End Sub

Protected Overridable Overloads Sub Validate(ByVal value As Object)

        '验证输入的数据是否存在

        If value Is Nothing Then

            Throw New ArgumentNullException()

        End If

End Sub

Protected Overridable Overloads Sub Validate(ByVal index As Integer, ByVal value As Object)

        '同时验证索引与数据元素

        Validate(index)

        Validate(value)

End Sub

 

----

声明:本文版权与解释权归韩睿所有,如需转载,请保留完整的内容及此声明。

QQ: 18349592

E-Mail: henry7685@hotmail.com

 请访问本人专栏:http://www.csdn.net/develop/author/netauthor/Latitude/

数据结构单链表的建立的使用

建立单链表的意义: 储存数据 一般就是用的数组,灵活性也是很好的,但是数组的大小必须在开始的时候预先定义好,在未知的情况下就很容易损耗空间; 单链表:...
  • woshinannan741
  • woshinannan741
  • 2015年10月03日 18:02
  • 2412

数据结构--链表(js版)

JavaScript版的数据结构--链表 链表是由一组节点组成的集合。每个节点都使用一个对象的引用指向它的后继,指向另一个节点的引用叫做链。链表一般有一个头节点。...
  • weixin_35987513
  • weixin_35987513
  • 2016年10月16日 17:39
  • 3149

笔试面试常考数据结构-单链表常用操作编程实现

单链表是笔试以及面试手写代码中常考的数据结构之一。下面实现了单链表的常见操作:创建单链表、删除节点、打印单链表(包括正向打印以及逆向打印)、反转单链表、找出单链表的倒数第K个节点、合并两个有序单链表等...
  • xiajun07061225
  • xiajun07061225
  • 2013年07月04日 20:50
  • 7972

数据结构—利用单链表操作城市信息

效果截图: 这个用了一些简单的窗体应用和我自己编写的单链表来完成。 首先我创建了城市信息的类用以保存城市的信息public class CityData { public int X;...
  • zp1996323
  • zp1996323
  • 2015年11月08日 22:20
  • 289

java数据结构—单链表的实现原理

再次学习数据额结构,看到前面的单链表,感觉里面的思路很不错,自己动手写代码尝试一下,果然一动手就发现自己并没有完全理解。这里主要记录我花了很长时间才理解的地方,不去考虑增删改查,我觉得这些功能在很多地...
  • lsjweiyi
  • lsjweiyi
  • 2017年03月08日 13:24
  • 190

面试题—数据结构之单链表详述(基本篇)

单链表的结构是数据结构中最简单的,它的每一个节点只有一个指向后一个节点的指针。 单链表节点的定义: [cpp] view plaincopyprint? typedef ...
  • luckyaslan
  • luckyaslan
  • 2012年06月07日 15:57
  • 1149

数据结构—单链表瞎搞

很多人对数据结构哭爹喊娘,望而却步,就起原因啊,还是对指针不熟悉,本人作为屌丝初级程序猿也谈起这个也一度恼火,工作后,弄点码农的工作,啥数据结构都是封装好点,根本没法触及,但是对这些基本的东西部了解也...
  • a935502404
  • a935502404
  • 2013年09月04日 18:48
  • 290

数据结构—单链表的排序以及逆置

1、单链表排序: 单链表的排序中,只需要交换两个节点的值即可,不需要改变指向 typedef struct student { int data; struct student *next; }nod...
  • xwchao2014
  • xwchao2014
  • 2015年05月05日 08:36
  • 346

面试数据结构篇—单链表常考点汇总

单链表是最基本的数据结构,由于其简单的构造以及相关操作代码的简短,特别受面试官的青睐,面试官可以通过在短短的15分钟内让你写出单链表的操作,而对你的代码能力进行判断。所以掌握单链表是重中之重,对常考的...
  • u013011270
  • u013011270
  • 2013年12月05日 17:44
  • 1126

面试题—数据结构之单链表详述(基本篇3)

单链表的正向排序,就是插入数据时就按从小到大排序。代码有注释很容易理解的://单链表的正向排序 node *InsertSort(void) { int data = 0; struct node...
  • shiren_Bod
  • shiren_Bod
  • 2011年07月25日 23:34
  • 1252
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Henry手记—.NET数据结构对象补遗之单链表(一)
举报原因:
原因补充:

(最多只允许输入30个字)