单链表的定义以及使用

链表是一种有序的列表,它的内容通常存储与内容分散的位置上。

一般链表的串联方式有两种:

一种是通过数组有序串联链表的列表元素,通常用到两个数组,一个数组存放数据,一个数组存放链接的关系。这种链表的缺点在于,在插于或者删除元素的时候,要频繁的搬动元素,而且数组的大小是固定的,使用缺乏弹性。

另一种则是动态内存配置的链表,它由许多的结点(Node)链接而成,每一个结点包含数据部分以及指向下一个结点的指针(引用)。

我们常说的“链表”一般就是指第二种。

.Net Framework里并没有加入链表的数据结构对象。所以我就自己动手实践了一下。

偶尔看到韩睿的“.NET数据结构对象补遗之单链表”,我没有看他后面的代码部分(他用的是VB.NET),我也来写一个单链表的类。

上面讲了,链表由许多个结点链接而成,那首先,我们要考虑的就是创建一个链表结点的类。

首先,一个结点应该有两个域,一个域用来存放数据,一个域则存放指向下一个结点的引用。

 1 /**/ /// <summary>
 2/// 结点对象的类。
 3/// </summary>

 4 public   class  ListNode
 5 {
 6    Field#region Field
 7    private ListNode _nextNode;
 8    private object _data;
 9    #endregion

10
11    Property#region Property
12    /**//// <summary>
13    /// 指向下一个结点的引用。
14    /// </summary>

15    public ListNode NextNode
16    {
17        get{return this._nextNode;}
18        set{this._nextNode = value;}
19    }

20
21    /**//// <summary>
22    /// 结点的数据项
23    /// </summary>

24    public object Data
25    {
26        get{return this._data;}
27        set{this._data = value;}
28    }

29    #endregion

30
31    Constructor#region Constructor
32    /**//// <summary>
33    /// 初始化一个结点。
34    /// </summary>
35    /// <param name="data">结点数据项。</param>
36    /// <param name="nextNode">指向下一个结点的引用。</param>

37    public ListNode(object data, ListNode nextNode)
38    {
39        this._data = data;
40        this._nextNode = nextNode;
41    }

42
43    /**//// <summary>
44    /// 初始化一个作为尾结点的结点。
45    /// </summary>
46    /// <param name="data">结点数据项。</param>

47    public ListNode(object data) : this(data, null)
48    {                
49    }

50    #endregion

51}


接下来,要考虑的就是创建一个链表类。先考虑一下链表类需要提供哪些操作。

首先,通过位置(索引)获取到对链表中的数据的访问,这对于列表数据结构而言是基本的实现内容,同样,针对链表元素的数据项的所在位置的索引,也是我们需要提供的实现方法之一。

昨天写了篇数组的东西,里面就说到数组的增、删、排效率很低,因为要频繁移动存储位置。而链表作为一种动态内存分配的列表数据结构,在添加、删除、排序方面没有了数组的先天不足,所以添加、删除、排序这几个方法肯定需要在我的这个链表类中得以实现。

从上面这些我首先想到的就是链表类可以从IList继承而来,IList是所有列表对象的抽象基类,也就是所有的列表对象都实现了System.Collections.IList这个接口。除了排序,上面提到的需要实现的方法在IList中都有定义。

同时在这里,我把刚才前面定义的那个结点类作为Nested Class内嵌在链表内中,提升这个结构的聚合性。

1 using  System.Collections;
2
3 public   class  SingleLinkedList : IList
4 {
5    public class ListNode
6    {
7        // .
8    }

9}

在开始为链表类的方法作具体实现之前,先定义几个基本的成员;
 1 /**/ /// <summary>
 2/// 链表的头结点。
 3/// </summary>
 4 protected  ListNode _head;
 5
 6 /**/ /// <summary>
 7/// 链表的尾结点。
 8/// </summary>

 9 protected  ListNode _tail ;
10
11 /**/ /// <summary>
12/// 链表结点的数量。
13/// </summary>

14 protected   int  _nodeCount  =   0 ;

以及一些基本的内部调用,比如验证结点的有效性,根据结点索引或数据内容查找结点等。
 1 /**/ /// <summary>
 2/// 判断指定索引是否超出链表结点索引上下限。
 3/// </summary>
 4/// <param name="index">结点的索引值。</param>

 5 protected   virtual   void  Validate( int  index)
 6 {
 7    if  (index < 0 || index >= this._nodeCount)
 8    {
 9        throw new ArgumentOutOfRangeException("索引越界");
10    }

11}

12
13 /**/ /// <summary>
14/// 判断结点值是否有效。
15/// </summary>
16/// <param name="value">结点的值。</param>

17 protected   virtual   void  Validate( object  value)
18 {
19    if (value == null)
20    {
21        throw new ArgumentNullException();
22    }

23}

24
25 /**/ /// <summary>
26/// 判断结点的索引以及值是否有效。
27/// </summary>
28/// <param name="index">结点的索引值。</param>
29/// <param name="value">结点的值。</param>

30 protected   virtual   void  Validate( int  index,  object  value)
31 {
32    this.Validate(index);
33    this.Validate(value);
34}

35
36 /**/ /// <summary>
37/// 通过结点索引查找结点对象。
38/// </summary>
39/// <param name="index">结点的索引值。</param>
40/// <returns>如果找到指定索引的结点,则返回该结点对象,否则返回null。</returns>

41 public   virtual  ListNode FindByIndex( int  index)
42 {
43    int tmpIndex = 0;
44
45    // 从首结点的下一个结点开始查找
46    ListNode current = this._head.NextNode;
47
48    ListNode returnValue = null;
49
50    do
51    {
52        if (tmpIndex == index)
53        {
54            returnValue = current;
55        }

56        else
57        {
58            tmpIndex ++;
59            current = current.NextNode;
60        }

61    }

62    while (current != null && returnValue == null);
63
64    return returnValue;
65
66}

67
68 /**/ /// <summary>
69/// 根据结点的值查找结点的索引。
70/// </summary>
71/// <param name="value">要查找的结点的值。</param>
72/// <returns>如果找到指定的结点,则返回该结点的索引,否则返回-1。</returns>

73 public   virtual   int  FindByValue( object  value)
74 {
75    int tmpIndex = 0;
76
77    // 从首结点的下一个结点开始查找
78    ListNode current = this._head.NextNode;
79
80    int returnValue = -1;
81
82    do 
83    {
84        if (value.Equals(current.Data))
85        {
86            returnValue = tmpIndex;
87        }

88        else
89        {
90            tmpIndex ++;
91            current = current.NextNode;
92        }

93    }

94    while (current != null && returnValue == -1);
95
96    return returnValue;
97}



到这里,准备工作做差不多了,开始为这个单链表提供具体实现的代码编写。 

基础实现部分

1. 添加结点元素(实现IList.Insert、IList.Add)

 1 public   void  Insert( int  index,  object  value)
 2 {
 3    // 首先验证结点值的有效性。
 4    this.Validate(index, value);
 5    
 6
 7    if (index == 0)
 8    {
 9        // 插入首结点前
10        this._head = new ListNode(value, this._head);
11    }

12    else
13    {
14        ListNode lastNode = this.FindByIndex(index - 1);
15
16        lastNode.NextNode = new ListNode(value, this.FindByIndex(index));
17    }

18
19    this._nodeCount += 1;
20
21    //this._version += 1;
22}

23
24 public   int  Add( object  value)
25 {
26    // 首先验证结点值的有效性。
27    this.Validate(value);
28
29    this._tail.NextNode = new ListNode(value);
30    this._tail = this._tail.NextNode;
31
32    this._nodeCount += 1;
33
34    return this._nodeCount - 1;
35
36    //this._version += 1;
37}


2. 删除结点元素(自定义一个RemoveNode、实现IList.RemoveAt、IList.Remove)

 1 protected   virtual   void  RemoveNode( int  index, ListNode node)
 2 {
 3    if (index == 0)
 4    {
 5        // 如果是首结点
 6        this._head = node.NextNode;
 7    }

 8    else
 9    {
10        ListNode lastNode = this.FindByIndex(index - 1);
11
12        lastNode.NextNode = node.NextNode;
13
14        if (node == this._tail)
15        {
16            // 如果是尾结点
17            this._tail = lastNode;
18        }

19    }

20
21    this._nodeCount -= 1;
22
23    //this._version += 1;
24}

25
26 public   void  RemoveAt( int  index)
27 {
28    // 首先验证结点值的有效性。
29    this.Validate(index);
30
31    this.RemoveNode(index, this.FindByIndex(index));
32}

33
34 public   void  Remove( object  value)
35 {
36    // 首先验证结点值的有效性。
37    this.Validate(value);
38
39    this.RemoveAt(this.FindByValue(value));
40}


3. 按索引获取结点以及根据结点值返回结点索引(实现IList索引器、IList.IndexOf)

 1 public   object   this [ int  index]
 2 {
 3    get
 4    {
 5        // 首先验证结点值的有效性。
 6        this.Validate(index);
 7
 8        return this.FindByIndex(index).Data;
 9    }

10    set
11    {
12        this.Insert(index, value);
13    }

14}

15
16 public   int  IndexOf( object  value)
17 {
18    // 首先验证结点值的有效性。
19    this.Validate(value);
20    // 根据结点值查找结点。
21    return this.FindByValue(value);
22}


4. 其他IList成员的实现

 1 public   bool  Contains( object  value)
 2 {
 3    // 首先验证结点值的有效性。
 4    if (this.IndexOf(value) > -1)
 5    {
 6        return true;
 7    }

 8    else
 9    {
10        return false;
11    }

12}

13
14 public   void  Clear()
15 {
16    this._head.NextNode = null;
17
18    this._tail = _head;
19
20    this._nodeCount = 0;
21
22    //this._version = 0;
23}



 

进阶功能实现

1. 复制(实现ICollection.CopyTo)

 1 public   void  CopyTo(Array array,  int  index)
 2 {
 3    if (array == null)
 4    {
 5        throw new NullReferenceException();
 6    }

 7    else if (index < 0)
 8    {
 9        throw new ArgumentOutOfRangeException();
10    }

11    else if (array.Rank != 1 || array.Length - index != this._nodeCount || array.Length <= index)
12    {
13        throw new ArgumentException();
14    }

15
16    ListNode node = this._head;
17
18    while (node.NextNode != null)
19    {
20        array[index] = node.NextNode.Data;
21        node = node.NextNode;
22        index++;
23    }

24    
25}

2. 对foreach的支持

VB中的For Each是个很强大的遍历方法,要实现这个功能,链表类必须实现IEnumerable接口,IList接口本身也是由该接口继承而来,所以这里不必要显示继承。C#语言从VB中吸取了这个非常实用的语句(写法为foreach)。对所有支持IEnumerable接口的类的实例,foreach语句使用统一的接口遍历其子项,使得以前冗长的for循环中繁琐的薄记工作完全由编译器自动完成。支持IEnumerable接口的类通常用一个内嵌类实现IEnumerator接口,并通过IEnumerable.GetEnumerator函数,允许类的使用者如foreach语句完成遍历工作。

为了对接口IEnumerable提供支持,我这里需要新建一个实现该接口的SingleLinkedListEnumerator类,定义如下:

 1 public   class  SingleLinkedListEnumerator : IEnumerator
 2 {
 3    protected int _index;
 4    protected SingleLinkedList _list;
 5    
 6    public SingleLinkedListEnumerator(SingleLinkedList list)
 7    {
 8        this._list = list;
 9        this._index = -1;
10    }

11
12    IEnumerator 成员#region IEnumerator 成员
13
14    public void Reset()
15    {
16        this._list = -1;
17    }

18
19    public object Current
20    {
21        get
22        {
23            // 验证索引的有效性。
24            if (this._index < -1 || this._index > this._list.Count - 1)
25            {
26                throw new ArgumentException("参数越界");
27            }

28            else if (this._index == -1)
29            {
30                throw new InvalidOperationException("在没有调用MoveNext前访问Current是无效的");
31            }

32            else if (this._index >= this._list.Count)
33            {
34                throw new InvalidOperationException("已到集合尾部,访问无效");
35            }

36
37            return this._list[this._index];
38        }

39    }

40
41    public bool MoveNext()
42    {
43        // 验证索引的有效性。
44        this._index ++;
45        if (this._index > this._list.Count - 1)
46        {
47            return false;
48        }

49        else
50        {
51            return true;
52        }

53    }

54
55    #endregion

56}

实现IEnumerable.GetEnumerator

1 public  IEnumerator GetEnumerator()
2 {
3    return new SingleLinkedListEnumerator(this);
4}

未完待续。。。

转载于:https://www.cnblogs.com/leonardleonard/archive/2007/03/21/1928186.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值