本文详细讲解了C#集合本质之链表的用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
链表的由来和定义
在现实生活中,我们把不同的商品放在一个购物车中。而在面向对象的世界里,有时候,也需要把不同类型的数据放到一起,组成一个集合。集合中的元素并不是彼此孤立的,在C#中,如何表达集合元素间的关系呢?
借助"自引用类"可以确立集合元素间的关系。比如有这样一个自引用类:
public class Node
{
public int Data{get;set;}
public Node Next{get;set;}
public Node(int dataValue)
{}
}
Node类的最大特点是:存在一个Node类型的属性,这个属性指向Node的另一个实例,Next属性也称为"引用链"。放到集合的场景中来说就是:把多个Node实例放到一个集合中,每一个Node实例包含一个Next属性指向下一个Node实例。而该集合中的最后一个Node实例会指向null。用图表示就是:
链表就是自引用类对象的线性集合,即序列。
由于每个自引用对象是由引用链链接起来,所以叫链表。堆栈与队列是约束版的链表,而二叉查找数是非线性数据结构。
链表的节点或元素虽然在逻辑上是连续的、线性的,当其内存不是连续存储的;数组元素在内存中是连续的,所以我们才可以通过索引来访问数组元素。
创建一个单向链表
首先创建一个节点,是一个自引用类:
namespace LinkedListLibrary
{
public class ListNode
{
//当前节点对象
public object Data { get; private set; }
//Next属性也称为链,指向另一个ListNode对象实例,这样就把2个ListNode对象实例链接起来了
public ListNode Next { get; set; }
public ListNode(object dataValue): this(dataValue, null)
{
}
public ListNode(object dataValue, ListNode nextNode)
{
Data = dataValue;
Next = nextNode;
}
}
}
再模拟一个链表,如下:
namespace LinkedListLibrary
{
public class List
{
private ListNode firstNode;
private ListNode lastNode;
private string name;
public List(string listName)
{
name = listName;
firstNode = lastNode = null;
}
public List() : this("list"){}
......
//如果第一个节点是null,那就说明集合为空
public bool IsEmpty()
{
return firstNode == null;
}
}
}
以上,如果第一个节点为null,那就说明该链表为空。List类提供了IsEmpty方法用来判断链表是否为空。List还包含另外5个重要的方法,下面展开来说。
在链表的的第一个节点前插入。
//在最前面插入元素、节点
public void InsertAtFront(object insertItem)
{
if (IsEmpty())//如果集合为空,加进来一个元素,相当于第一个节点和第二个节点相同,都是新加的元素
{
firstNode = lastNode = new ListNode(insertItem);
}
else //如果集合不为空,第一个节点就是新加的元素,原先的第一个节点变为下一个节点
{
firstNode = new ListNode(insertItem, firstNode);
}
}
以上,当集合不为空的情况下,实际上是把新添加的节点设为第一个节点,并把新的第一个节点的引用链指向原先的第一个节点。
在链表的最后一个节点后插入。
public void InsertAtBack(object insertItem)
{
if (IsEmpty())//如果原先集合为空,第一个节点和最后一个节点就是新加的节点
{
firstNode = lastNode = new ListNode(insertItem);
}
else//如果原先的集合不为空,最后一个节点的属性值就是新加的节点
{
lastNode = lastNode.Next = new ListNode(insertItem);
}
}
以上,当集合不为空的情况下,实际上是把新添加的节点设置成最后一个节点,并把新的最后一个节点的引用链指向null。
移除链表最前面的节点。
//移除最前面的元素、节点
//即重新设置第一个节点的Next属性
public object RemoveFromFront()
{
if (IsEmpty())
throw new EmptyListException(name);
//从第一个节点中取出节点对象
object removeItem = firstNode.Data;
if (firstNode == lastNode) //如果集合中只有一个元素
{
firstNode = lastNode = null;
}
else //正常情况下,把firstNode的Next属性所指向的节点赋值给第一个节点
{
firstNode = firstNode.Next;
}
return removeItem;
}
以上,本质是把原先排在第二位置的节点设置成第一个节点。
移除链表最后面的节点。
//移除最后面的元素、节点
public object RemoveFromBack()
{
if (IsEmpty())
{
throw new EmptyListException();
}
//从最后一个节点中获取节点对象
object removeItem = lastNode.Data;
if (firstNode == lastNode)//如果当前集合只有一个节点
{
firstNode = lastNode = null;
}
else
{
//先把第一个节点作为当前节点
ListNode current = firstNode;
//改变除最后一个节点之外的节点的值
while (current.Next != lastNode)
{
current = current.Next;
}
//最后current变成倒数第二个节点
lastNode = current;
current.Next = null;//最后一个节点的Next属性为null,即没有指向另一个节点
}
return removeItem;
}
以上,从第一个节点开始,一直循环到倒数第二个节点,current就像一个指针,每指到一个节点,就把该节点的下面一个节点设置为当前节点。最后,把倒数第二个节点设置为最后一个节点。 把Current的引用链设置为null,让其能被垃圾回收机制回收。
打印链表。
//打印显示
public void Display()
{
if (IsEmpty())
{
Console.WriteLine("集合" + name + "为空");
}
else
{
Console.WriteLine("集合的名称是:" + name);
//先把第一个节点作为当前节点
ListNode current = firstNode;
while (current != null)
{
//把当前节点对象打印出来
Console.Write(current.Data + " ");
//把下一个节点设置为当前节点
current = current.Next;
}
Console.WriteLine("\n");
}
}
以上,从第一个节点开始,一直循环到最后一个节点,current就像一个指针,每打印一个节点,就把当前节点设置为下一个节点,一直循环下去。
EmptyListException用来抛出链表为空的异常。
namespace LinkedListLibrary
{
public class EmptyListException : Exception
{
public EmptyListException() : base("当前集合为空"){}
public EmptyListException(string name) : base("集合" + name + "为空"){}
public EmptyListException(string exception, Exception inner) : base(exception, inner){}
}
}
客户端调用:
using LinkedListLibrary;
namespace ListTest
{
class Program
{
static void Main(string[] args)
{
List list = new List();
bool aBoolean = true;
char aChar = 'a';
int anInt = 12;
string aStr = "hi";
list.InsertAtFront(aBoolean);
list.Display();
list.InsertAtFront(aChar);
list.Display();
list.InsertAtBack(anInt);
list.Display();
list.InsertAtBack(aStr);
list.Display();
object removeObject;
try
{
removeObject = list.RemoveFromFront();
Console.WriteLine(removeObject + "被删除了...");
list.Display();
removeObject = list.RemoveFromFront();
Console.WriteLine(removeObject + "被删除了...");
list.Display();
removeObject = list.RemoveFromBack();
Console.WriteLine(removeObject + "被删除了...");
list.Display();
removeObject = list.RemoveFromBack();
Console.WriteLine(removeObject + "被删除了...");
list.Display();
}
catch (EmptyListException emptyListException)
{
Console.Error.WriteLine("\n" + emptyListException);
}
Console.ReadKey();
}
}
}
其它链表
以上,创建的是单向链表,其特点是第一个节点开始包含引用链,每个节点的引用链指向下一个节点,最后一个节点的引用链为null。单向链表只能从一个方向遍历。
环形单向链表与单向链表的区别是:其最后一个节点的引用链指向第一个节点。环形单向链表也只能从一个方向遍历,只不过遍历到最后一个节点后,又回到第一个节点重新开始遍历。
双向链表的第一个节点只包含指向下一个节点的引用链,最后一个节点只包含指向上一个节点的引用链,其它节点同时包含指向前一个节点和后一个节点的引用链。双向链表支持向前和向后遍历。
环形双向链表与双向链表的区别是:第一个节点向后引用链指向最后一个节点,而最后一个节点的向前引用链指向第一个节点。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对得得之家的支持。如果你想了解更多相关内容请查看下面相关链接
本文标题为:C#集合本质之链表的用法详解
基础教程推荐
- C#控制台实现飞行棋小游戏 2023-04-22
- C#类和结构详解 2023-05-30
- ZooKeeper的安装及部署教程 2023-01-22
- linux – 如何在Debian Jessie中安装dotnet core sdk 2023-09-26
- unity实现动态排行榜 2023-04-27
- C# List实现行转列的通用方案 2022-11-02
- C# windows语音识别与朗读实例 2023-04-27
- 一个读写csv文件的C#类 2022-11-06
- C# 调用WebService的方法 2023-03-09
- winform把Office转成PDF文件 2023-06-14