链表是一种有序的列表,它的内容通常存储与内容分散的位置上。
一般链表的串联方式有两种:
一种是通过数组有序串联链表的列表元素,通常用到两个数组,一个数组存放数据,一个数组存放链接的关系。这种链表的缺点在于,在插于或者删除元素的时候,要频繁的搬动元素,而且数组的大小是固定的,使用缺乏弹性。
另一种则是动态内存配置的链表,它由许多的结点(Node)链接而成,每一个结点包含数据部分以及指向下一个结点的指针(引用)。
我们常说的“链表”一般就是指第二种。
.Net Framework里并没有加入链表的数据结构对象。所以我就自己动手实践了一下。
偶尔看到韩睿的“.NET数据结构对象补遗之单链表”,我没有看他后面的代码部分(他用的是VB.NET),我也来写一个单链表的类。
上面讲了,链表由许多个结点链接而成,那首先,我们要考虑的就是创建一个链表结点的类。
首先,一个结点应该有两个域,一个域用来存放数据,一个域则存放指向下一个结点的引用。
1![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
2
/// 结点对象的类。
3
/// </summary> 4
public class ListNode
5![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Field#region Field
7
private ListNode _nextNode;
8
private object _data;
9
#endregion
10
11![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Property#region Property
12![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
13
/// 指向下一个结点的引用。
14
/// </summary>
15
public ListNode NextNode
16![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
17![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{return this._nextNode;}
18![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{this._nextNode = value;}
19
}
20
21![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
22
/// 结点的数据项
23
/// </summary>
24
public object Data
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
26![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{return this._data;}
27![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
set
{this._data = value;}
28
}
29
#endregion
30
31![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
Constructor#region Constructor
32![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
33
/// 初始化一个结点。
34
/// </summary>
35
/// <param name="data">结点数据项。</param>
36
/// <param name="nextNode">指向下一个结点的引用。</param>
37
public ListNode(object data, ListNode nextNode)
38![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
39
this._data = data;
40
this._nextNode = nextNode;
41
}
42
43![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
44
/// 初始化一个作为尾结点的结点。
45
/// </summary>
46
/// <param name="data">结点数据项。</param>
47
public ListNode(object data) : this(data, null)
48![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
49
}
50
#endregion
51
}
接下来,要考虑的就是创建一个链表类。先考虑一下链表类需要提供哪些操作。
首先,通过位置(索引)获取到对链表中的数据的访问,这对于列表数据结构而言是基本的实现内容,同样,针对链表元素的数据项的所在位置的索引,也是我们需要提供的实现方法之一。
昨天写了篇数组的东西,里面就说到数组的增、删、排效率很低,因为要频繁移动存储位置。而链表作为一种动态内存分配的列表数据结构,在添加、删除、排序方面没有了数组的先天不足,所以添加、删除、排序这几个方法肯定需要在我的这个链表类中得以实现。
从上面这些我首先想到的就是链表类可以从IList继承而来,IList是所有列表对象的抽象基类,也就是所有的列表对象都实现了System.Collections.IList这个接口。除了排序,上面提到的需要实现的方法在IList中都有定义。
同时在这里,我把刚才前面定义的那个结点类作为Nested Class内嵌在链表内中,提升这个结构的聚合性。
1
using System.Collections;
2
3
public class SingleLinkedList : IList
4![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
5
public class ListNode
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
7
//
.
8
}
9
}
在开始为链表类的方法作具体实现之前,先定义几个基本的成员;
1![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
2
/// 链表的头结点。
3
/// </summary> 4
protected ListNode _head;
5
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
7
/// 链表的尾结点。
8
/// </summary> 9
protected ListNode _tail;
10
11![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
12
/// 链表结点的数量。
13
/// </summary> 14
protected int _nodeCount = 0;
以及一些基本的内部调用,比如验证结点的有效性,根据结点索引或数据内容查找结点等。
1![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
2
/// 判断指定索引是否超出链表结点索引上下限。
3
/// </summary>
4
/// <param name="index">结点的索引值。</param> 5
protected virtual void Validate(int index)
6![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
7
if (index < 0 || index >= this._nodeCount)
8![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
9
throw new ArgumentOutOfRangeException("索引越界");
10
}
11
}
12
13![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
14
/// 判断结点值是否有效。
15
/// </summary>
16
/// <param name="value">结点的值。</param> 17
protected virtual void Validate(object value)
18![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
19
if (value == null)
20![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
21
throw new ArgumentNullException();
22
}
23
}
24
25![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
26
/// 判断结点的索引以及值是否有效。
27
/// </summary>
28
/// <param name="index">结点的索引值。</param>
29
/// <param name="value">结点的值。</param> 30
protected virtual void Validate(int index, object value)
31![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
32
this.Validate(index);
33
this.Validate(value);
34
}
35
36![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
37
/// 通过结点索引查找结点对象。
38
/// </summary>
39
/// <param name="index">结点的索引值。</param>
40
/// <returns>如果找到指定索引的结点,则返回该结点对象,否则返回null。</returns> 41
public virtual ListNode FindByIndex(int index)
42![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
43
int tmpIndex = 0;
44
45
// 从首结点的下一个结点开始查找
46
ListNode current = this._head.NextNode;
47
48
ListNode returnValue = null;
49
50
do
51![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
52
if (tmpIndex == index)
53![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
54
returnValue = current;
55
}
56
else
57![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
58
tmpIndex ++;
59
current = current.NextNode;
60
}
61
}
62
while (current != null && returnValue == null);
63
64
return returnValue;
65
66
}
67
68![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
/**//// <summary>
69
/// 根据结点的值查找结点的索引。
70
/// </summary>
71
/// <param name="value">要查找的结点的值。</param>
72
/// <returns>如果找到指定的结点,则返回该结点的索引,否则返回-1。</returns> 73
public virtual int FindByValue(object value)
74![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
75
int tmpIndex = 0;
76
77
// 从首结点的下一个结点开始查找
78
ListNode current = this._head.NextNode;
79
80
int returnValue = -1;
81
82
do
83![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
84
if (value.Equals(current.Data))
85![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
86
returnValue = tmpIndex;
87
}
88
else
89![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
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![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
// 首先验证结点值的有效性。
4
this.Validate(index, value);
5
6
7
if (index == 0)
8![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
9
// 插入首结点前
10
this._head = new ListNode(value, this._head);
11
}
12
else
13![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
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![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
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![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
if (index == 0)
4![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
5
// 如果是首结点
6
this._head = node.NextNode;
7
}
8
else
9![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
10
ListNode lastNode = this.FindByIndex(index - 1);
11
12
lastNode.NextNode = node.NextNode;
13
14
if (node == this._tail)
15![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
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![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
28
// 首先验证结点值的有效性。
29
this.Validate(index);
30
31
this.RemoveNode(index, this.FindByIndex(index));
32
}
33
34
public void Remove(object value)
35![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
36
// 首先验证结点值的有效性。
37
this.Validate(value);
38
39
this.RemoveAt(this.FindByValue(value));
40
}
3. 按索引获取结点以及根据结点值返回结点索引(实现IList索引器、IList.IndexOf)
1
public object this[int index]
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
get
4![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
5
// 首先验证结点值的有效性。
6
this.Validate(index);
7
8
return this.FindByIndex(index).Data;
9
}
10
set
11![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
12
this.Insert(index, value);
13
}
14
}
15
16
public int IndexOf(object value)
17![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
18
// 首先验证结点值的有效性。
19
this.Validate(value);
20
// 根据结点值查找结点。
21
return this.FindByValue(value);
22
}
4. 其他IList成员的实现
1
public bool Contains(object value)
2![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
3
// 首先验证结点值的有效性。
4
if (this.IndexOf(value) > -1)
5![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
6
return true;
7
}
8
else
9![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
10
return false;
11
}
12
}
13
14
public void Clear()
15![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
16
this._head.NextNode = null;
17
18
this._tail = _head;
19
20
this._nodeCount = 0;
21
22
//this._version = 0;
23
}