单链表与顺序链表不同,顺序链表在声明时在内存中开辟一块连续的存储空间进行链表数据项的保存。所以单链表的项只需要保存其数据,根据数据所在的序号进行访问直接由链表类控制,因为屋里保存区域连续,所以能够很方便实现这些数据的访问,插入和删除。
单链表在内存中不适用连续的空间存储,所以单链表的项实际保存两个信息,一个信息为该项的数据项,另外一个信息为指定下一项。在访问时,我们只需要知道单链表的起始项,起始项就是单链表中没有被任何项指向的项,根据起始项指向的下一项一次类推,得到我们需要的链表项,图示如下:
从上面的单链表示意图可以看到,链表首项指向第二项,第二项指向第三项,最后一项不指向任何项,所以在构造单链表之前,我们需要首先构造单链表的项,这里我们就疑问了,为什么在顺序链表中不需要构造链表项?刚才已经说了,顺序链表只需要保存值本身即可,所以使用泛型类即可满足需求,下面给出单链表项的类:
/// <summary>
/// 单向连接节点类 /// </summary> /// <typeparam name="T"></typeparam> public class Node<T> { private T data; //数据域 private Node<T> next; //引用域 //构造函数 public Node(T data, Node<T> next) { this.data = data; this.next = next; } //构造函数 public Node(T data) { this.data = data; this.next = null; } //构造函数 public Node(Node<T> next) { this.next = next; } //构造函数 public Node() { this.data = default(T); this.next = null; } public T Data { set { data = value; } get { return data; } } public Node<T> Next { get { return next; } set { next = value; } } }
链表项的类很简单,声明了两个属性分别为Data和Next,分别表示链表项的两个存储信息,声明相对应的构造函数。
下面是单链表项的代码:
class SinglyLink<T> : IListDS<T>
{
private Node<T> head;
public Node<T> Head
{
set { head = value; }
get { return head; }
}
/// <summary>
/// 求单链表长度,正确
/// </summary>
/// <returns></returns>
public int GetLength()
{
int length = 0;
Node<T> P = head;
while (P != null)
{
P = P.Next;
length++;
}
return length;
}
/// <summary>
/// 清楚单链表所有项
/// </summary>
public void Clear()
{
head = null;
}
/// <summary>
/// 判断单链表是否为空
/// </summary>
/// <returns></returns>
public bool IsEmpty()
{
if (head == null)
return true;
else
return false;
}
/// <summary>
/// 判断单链表是否已满,因为单链表不存在最大长度的限制,所以单链表永远不会满
/// </summary>
/// <returns></returns>
public bool IsFull()
{
return false;
}
/// <summary>
/// 在单链表的最末尾添加新项,有以下几种情况
/// 当链表为空时,只需要将添加项赋予链表头即可
/// 当链表不为空时,需要先查找到链表的最末项,然后将最末项的末尾
/// </summary>
/// <param name="item"></param>
public void Append(T item)
{
if (head == null)
{
head = new Node<T>(item);
return;
}
Node<T> p = head;
while (p.Next != null)
{
p = p.Next;
}
p.Next = new Node<T>(item);
}
/// <summary>
/// 在单链表指定的位置插入新项,当插入时,存在以下几种情况
/// 当指定的位置超出单链表的最小长度和现有长度时,抛出异常
/// 当在单链表的起始位置插入新项时,只需要将新项的Next属性设为现有的head
/// 当在单链表的末尾位置插入新项时,只需要在链表的最末位Append新项
/// 当在单链表的中间位置插入新项时,需要首先获取插入位置的前一项,将其Next属性设置为新项,然后将新项Next属性设置为中间断开的下一项
/// </summary>
/// <param name="item"></param>
/// <param name="index"></param>
public void Insert(T item, int index)
{
if (index <= 0 || index > GetLength() + 1)
throw new Exception("指定位置不在链表长度内");
if (index == 1)
{
if (this.IsEmpty())
{
this.head = new Node<T>(item);
return;
}
Node<T> node = this.head;
this.head = new Node<T>(item);
this.head.Next = node;
return;
}
if (index == GetLength() + 1)
{
this.Append(item);
return;
}
Node<T> tempItem = new Node<T>(item);
Node<T> tempLast = this.head;
Node<T> tempNext = default(Node<T>);
for (int i = 0; i < index - 2; i++)
{
tempLast = tempLast.Next;
}
tempNext = tempLast.Next;
tempLast.Next = tempItem;
tempItem.Next = tempNext;
}
/// <summary>
/// 删除列表中指定顺序的项,可能有以下几种情况
/// 顺序的序列号超出链表的范围,抛出异常
/// 顺序的序号为列表首项,只需要将第二项设置为首相即可
/// 顺序的序号为列表尾箱,只需要将倒数第二项后的Next属性设置为空
/// 顺序的序号在列表中时,需要删除该项,然后设置前项的Next属性为下一项
/// </summary>
/// <param name="index"></param>
public void Delete(int index)
{
if (index <= 0 || index > GetLength())
throw new Exception("指定位置不在链表长度内");
if (index == 1)
{
this.head = head.Next;
return;
}
if (index == GetLength())
{
Node<T> node = this.head;
for (int i = 0; i < index - 2; i++)
{
node = node.Next;
}
node.Next = null;
return;
}
Node<T> tempItem = head;
Node<T> tempNext = default(Node<T>);
for (int i = 0; i < index - 2; i++)
{
tempItem = tempItem.Next;
}
tempNext = tempItem.Next;
tempNext = tempNext.Next;
tempItem.Next = tempNext;
}
/// <summary>
/// 根据用户输入的列表项序号获取列表项,可能遇到以下情况
/// 当输入序号超出列表索引范围时,抛出异常
/// 当输入序号为首项时,返回head
/// 当输入序号为其他时,查找对应的项返回
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T GetItem(int index)
{
if (index < 0 || index > GetLength() - 1)
throw new Exception("指定位置不在链表长度内");
if (index == 0)
return this.head.Data;
Node<T> tempItem = head;
for (int i = 0; i < index - 1; i++)
{
tempItem = tempItem.Next;
}
return tempItem.Next.Data;
}
/// <summary>
/// 根据用户输入的值返回该值在链表的位置
/// 有两种情况,当链表为空或链表不为空
/// 顺序查找链表的项,当找到匹配项时,返回,不再查找后面的项是否仍有匹配
/// 当搜索完整个链表仍没有找到值时,返回-1。表示没有找到任何值
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public int Locate(T value)
{
if (IsEmpty())
throw new Exception("链表为空,无法查找");
for (int i = 0; i < GetLength(); i++)
{
if (GetItem(i).Equals(value))
{
return i;
}
}
return -1;
}
}