链表
链表
前言:LinkedList<T>是一个双向链表,其元素指向他前面和后面的元素。如下图所示,这样一来,通过移动到下一个元素可以正向的遍历整个链表。通过移动到前一个元素可以反向遍历整个链表。
链表的优点:如果将元素插入列表的中间位置,使用链表就会非常快。在插入一个元素时,只需要修改上一个元素的Next引用和下一个元素的Previous引用,使他们引用所插入的元素。在List<T>类中,插入一个元素时,需要移动该元素后面的所有的元素。
链表的缺点:链表的元素只能一个接一个的访问。这需要较长的时间来查找位于链表中间或者尾部的元素。
链表不能在列表中仅存储元素。存储元素时,链表还必须促成农户每个元素的下一个元素和上一个元素的信息。这就是LinkedList<T>包含LinkedListNode<T>类型的元素的原因。使用LinkedListNode<T>类 ,可以获得列表中的下一个元素和上一个元素。LinkedListNode<T>定义了属性List、Next、Previous、和Value。List属性返回与节点相关的ListedList<T>对象,Next和Previous属性用于遍历链表。访问当前节点之后和之前的节点。Value返回与节点相关的元素,其类型是T。
LinkedList<T>类定义的成员可以访问链表中的第一个和最后一个元素(First和Last)、在指定的位置上插入元素(AddAfter()、AddBefore()、AddFirst()、AddLast()方法)删除指定位置的元素(Remove()、RemoveFirst()、RemoveLast())、从链表的开头(Find()方法)或者结尾(FindLast()方法)开始搜索元素。
我们实例的应用程序使用了一个链表和一个列表。链表包含文档,这与上一个队列例子相同,但文档有 一个额外的优先级。在链表中,文档按照优先级来排序。如果多个文档的优先级相同,这些元素就按照文档的插入的时间来排序。
在链表中添加新文档时,他们应放在优先级相同的最后一个文档的后面。集合LinkedList<Document>包含LinkedListNode<Document>类型的元素。LinkedListNode<T>类添加了Next和Previous属性。使搜索过程中能从一个节点移动到下一个节点上。要引用这些元素,应把List<T>定义为List<LinkedListNode<Document>>。
在上面的例子中,Document类扩展为包含优先级。优先级用类的构造函数设置:
/// <summary> /// 文档类 /// </summary> public class Document { public string Title { get; set; } //标题 public string Content { get; set; } //内容 public byte Priority { get; set; } //优先级 /// <summary> /// 构造函数的初始化赋值 /// </summary> /// <param name="title"></param> /// <param name="content"></param> /// <param name="priority"></param> public Document(string title, string content, byte priority) { this.Title = title; this.Content = content; this.Priority = priority; } }
解决方案的核心是PriorityDocumentManager类。这个类很容易使用。在这个类的公共接口中,可以把新的Document元素添加到链表中,可以检索第一个文档。为了便于测试,它还提供了一个方法,在元素链接到链表中,该方法可以显示集合中的所有的元素。
PriorityDocumentManage类包含两个集合。LinkedList<Document>类型的集合包含所有的文档。List<LinkedListNode<Document>>类型的集合包含最多10个元素的引用。它们是添加指定优先级的新文档的入口点。这两个集合变量都用PriorityDocumentManager类的构造函数来进行实例化。列表集合也用null初始化。
/// <summary> /// 优先级文档管理器 /// </summary> public class PriorityDocumentManager { private readonly LinkedList<Document> _documentList; //双向链接列表集合 private readonly List<LinkedListNode<Document>> _priorityNodes; //优先级节点 /// <summary> /// 构造函数初始化 /// </summary> public PriorityDocumentManager() { _documentList = new LinkedList<Document>(); _priorityNodes = new List<LinkedListNode<Document>>(10); for (int i = 0; i < 10; i++) { //添加空的节点 _priorityNodes.Add(new LinkedListNode<Document>(null)); } } /// <summary> /// 添加文档 /// </summary> /// <param name="d"></param> public void AddDocument(Document d) { if (d == null) throw new ArgumentException("d"); AddDocumentToPriorityNode(d, d.Priority); } /// <summary> /// 显示增加的节点 /// </summary> public void DisplayAddNodes() { foreach (var doc in _documentList) { Console.WriteLine("优先级:{0},title:{1}", doc.Priority, doc.Title); } } /// <summary> /// 从链表中返回第一个文档(优先级最高的文档),并从链表中删除它. /// </summary> /// <returns></returns> public Document GetDocument() { Document doc = _documentList.First.Value; return doc; } /// <summary> /// 将文档添加到优先节点 /// </summary> /// <param name="doc">文档对象</param> /// <param name="priority">优先级</param> private void AddDocumentToPriorityNode(Document doc, int priority) { if (priority > 9 || priority < 0) { throw new AggregateException("优先级必须是0到9之间的数字"); } //获取节点中包含的值,如果是空的话 if (_priorityNodes[priority].Value == null) { //递减优先级 --priority; //如果优先级大于零的话 递归检查 if (priority >= 0) { //检查下一个较低的优先级 AddDocumentToPriorityNode(doc, priority); } //现在没有优先级或者更低的优先级 将新文档添加到末尾 else { //在结尾处添加新节点 _documentList.AddLast(doc); //将最后一个节点赋给优先级节点 _priorityNodes[doc.Priority] = _documentList.Last; } } //一个优先级节点存在 else { //得到节点:当执行到第三个的值的时候,首先拿到上一个值的节点的值 LinkedListNode<Document> prioNode = _priorityNodes[priority]; //如果具有相同的优先级节点 if (priority == doc.Priority) { //在指定的现有节点后添加包含指定值得新节点 _documentList.AddAfter(prioNode, doc); //将优先级节点设置为具有相同优先级的最后一个节点 _priorityNodes[doc.Priority] = prioNode.Next; } //只有具有较低优先级的优先级节点存在 else { //获得较低优先级的第一个节点 LinkedListNode<Document> firstPrioNode = prioNode; while (firstPrioNode.Previous != null && firstPrioNode.Previous.Value.Priority == prioNode.Value.Priority) { firstPrioNode = prioNode.Previous; prioNode = firstPrioNode; } //在指定的现有的节点前添加新值 _documentList.AddBefore(firstPrioNode, doc); //将优先值节点设置为新值 _priorityNodes[doc.Priority] = firstPrioNode.Previous; } } } }
在Main方法中,PriorityDocumentManager类用于说明其功能。在链表中添加8个优先级不同的新文档。在显示整个链表。
static void Main(string[] args) { var pmd = new PriorityDocumentManager(); pmd.AddDocument(new Document("one", "Sample", 8)); pmd.AddDocument(new Document("four", "Sample", 8)); pmd.AddDocument(new Document("two", "Sample", 3)); pmd.AddDocument(new Document("three", "Sample", 4)); pmd.AddDocument(new Document("five", "Sample", 1)); pmd.AddDocument(new Document("six", "Sample", 9)); pmd.AddDocument(new Document("seven", "Sample", 1)); pmd.AddDocument(new Document("eight", "Sample", 1)); pmd.DisplayAddNodes(); Console.ReadKey(); }
在处理好的结果中,文档先按优先级排序,在按文档添加文档的时间排序。