C#数据结构之双向链表(DbLinkList)实例详解

这篇文章主要介绍了C#数据结构之双向链表(DbLinkList),结合实例形式较为详细的讲解了双向链表的概念及C#实现双向链表的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下

本文实例讲述了C#数据结构之双向链表(DbLinkList)。分享给大家供大家参考,具体如下:

这是继上一篇《C#数据结构之单链表(LinkList)实例详解》的继续,对于双向链接,节点上除了Next属性外,还要有Prev属性用来指向前一个节点,DbNode定义如下:

 namespace 线性表 { public class DbNode { private T data; private DbNode prev; private DbNode next; public DbNode(T data, DbNode next,DbNode prev) { this.data = data; this.next = next; this.prev = prev; } public DbNode(T data, DbNode next) { this.data = data; this.next = next; this.prev = null; } public DbNode(DbNode next) { this.data = default(T); this.next = next; this.prev = null; } public DbNode(T data) { this.data = data; this.next = null; this.prev = null; } public DbNode() { data = default(T); next = null; prev = null; } public T Data { set { this.data = value; } get { return this.data; } } public DbNode Prev { get { return prev; } set { prev = value; } } public DbNode Next { get { return next; } set { next = value; } } } } 

双链表的插入操作要稍微复杂一点,示意图如下:

同样对于删除操作,也要额外处理prev指向

完整实现DbLinkList

 using System; using System.Text; namespace 线性表 { public class DbLinkList : IListDS { private DbNode head; public DbNode Head { get { return head; } set { head = value; } } public DbLinkList() { head = null; } ///  /// 类索引器 ///  ///  ///  public T this[int index] { get { return this.GetItemAt(index); } } ///  /// 返回单链表的长度 ///  ///  public int Count() { DbNode p = head; int len = 0; while (p != null) { len++; p = p.Next; } return len; } ///  /// 清空 ///  public void Clear() { head = null; } ///  /// 是否为空 ///  ///  public bool IsEmpty() { return head == null; } ///  /// 在最后附加元素 ///  ///  public void Append(T item) { DbNode d = new DbNode(item); DbNode n = new DbNode(); if (head == null) { head = d; return; } n = head; while (n.Next != null) { n = n.Next; } n.Next = d; d.Prev = n; } //前插 public void InsertBefore(T item, int i) { if (IsEmpty() || i <0) { Console.WriteLine("List is empty or Position is error!"); return; } //在最开头插入 if (i == 0) { DbNode q = new DbNode(item); q.Next = head;//把"头"改成第二个元素 head.Prev = q; head = q;//把自己设置为"头" return; } DbNode n = head; DbNode d = new DbNode(); int j = 0; //找到位置i的前一个元素d while (n.Next != null && j  q = new DbNode(item); n.Next = q; q.Prev = n; q.Next = null; } else { if (j == i) { DbNode q = new DbNode(item); d.Next = q; q.Prev = d; q.Next = n; n.Prev = q; } } } ///  /// 在位置i后插入元素item ///  ///  ///  public void InsertAfter(T item, int i) { if (IsEmpty() || i <0) { Console.WriteLine("List is empty or Position is error!"); return; } if (i == 0) { DbNode q = new DbNode(item); q.Next = head.Next; head.Next.Prev = q; head.Next = q; q.Prev = head; return; } DbNode p = head; int j = 0; while (p != null && j  q = new DbNode(item); q.Next = p.Next; if (p.Next != null) { p.Next.Prev = q; } p.Next = q; q.Prev = p; } else { Console.WriteLine("Position is error!"); } } ///  /// 删除位置i的元素 ///  ///  ///  public T RemoveAt(int i) { if (IsEmpty() || i <0) { Console.WriteLine("Link is empty or Position is error!"); return default(T); } DbNode q = new DbNode(); if (i == 0) { q = head; head = head.Next; head.Prev = null; return q.Data; } DbNode p = head; int j = 0; while (p.Next != null && j  /// 获取指定位置的元素 ///  ///  ///  public T GetItemAt(int i) { if (IsEmpty()) { Console.WriteLine("List is empty!"); return default(T); } DbNode p = new DbNode(); p = head; if (i == 0) { return p.Data; } int j = 0; while (p.Next != null && j  p = new DbNode(); p = head; int i = 0; while (!p.Data.Equals(value) && p.Next != null) { p = p.Next; i++; } return i; } ///  /// 元素反转 ///  public void Reverse() { DbLinkList result = new DbLinkList(); DbNode t = this.head; result.Head = new DbNode(t.Data); t = t.Next; //(把当前链接的元素从head开始遍历,逐个插入到另一个空链表中,这样得到的新链表正好元素顺序跟原链表是相反的) while (t!=null) { result.InsertBefore(t.Data, 0); t = t.Next; } this.head = result.head;//将原链表直接挂到"反转后的链表"上 result = null;//显式清空原链表的引用,以便让GC能直接回收 } //得到某个指定的节点(为了下面测试从后向前遍历) private DbNode GetNodeAt(int i){ if (IsEmpty()) { Console.WriteLine("List is empty!"); return null; } DbNode p = new DbNode(); p = head; if (i == 0) { return p; } int j = 0; while (p.Next != null && j  /// 测试用prev属性从后面开始遍历 ///  ///  public string TestPrevErgodic() { DbNode tail = GetNodeAt(Count() - 1); StringBuilder sb = new StringBuilder(); sb.Append(tail.Data.ToString() + ","); while (tail.Prev != null) { sb.Append(tail.Prev.Data.ToString() + ","); tail = tail.Prev; } return sb.ToString().TrimEnd(','); } public override string ToString() { StringBuilder sb = new StringBuilder(); DbNode n = this.head; sb.Append(n.Data.ToString() + ","); while (n.Next != null) { sb.Append(n.Next.Data.ToString() + ","); n = n.Next; } return sb.ToString().TrimEnd(','); } } } 

测试代码片段:

 Console.WriteLine("-------------------------------------"); Console.WriteLine("双链表测试开始..."); DbLinkList dblink = new DbLinkList(); dblink.Head = new DbNode("x"); dblink.InsertBefore("w", 0); dblink.InsertBefore("v", 0); dblink.Append("y"); dblink.InsertBefore("z", dblink.Count()); Console.WriteLine(dblink.Count());//5 Console.WriteLine(dblink.ToString());//v,w,x,y,z Console.WriteLine(dblink[1]);//w Console.WriteLine(dblink[0]);//v Console.WriteLine(dblink[4]);//z Console.WriteLine(dblink.IndexOf("z"));//4 Console.WriteLine(dblink.RemoveAt(2));//x Console.WriteLine(dblink.ToString());//v,w,y,z dblink.InsertBefore("x", 2); Console.WriteLine(dblink.ToString());//v,w,x,y,z Console.WriteLine(dblink.GetItemAt(2));//x dblink.Reverse(); Console.WriteLine(dblink.ToString());//z,y,x,w,v dblink.InsertAfter("1", 0); dblink.InsertAfter("2", 1); dblink.InsertAfter("6", 5); dblink.InsertAfter("8", 7); dblink.InsertAfter("A", 10);//Position is error! Console.WriteLine(dblink.ToString()); //z,1,2,y,x,w,6,v,8 string _tail = dblink.GetItemAt(dblink.Count()-1); Console.WriteLine(_tail); Console.WriteLine(dblink.TestPrevErgodic());//8 Console.ReadKey(); //8,v,6,w,x,y,2,1,z 

当然从上面的测试代码中,似乎并不能看出双链表的优点,双链表的好处在于,如果需要在链表中,需要通过某个节点得到它的前驱节点时,双链表直接用prev属性就能找到;而单链表要做到这一点,必须再次从Head节点开始一个一个用Next向下找,这样时间复杂度从O(n)降到O(1),显然更有效率。

注:如果把双链表再做一下改造,让头尾接起来,即Head的Prev属性指向最后一个节点(就叫做Tail吧),同时把Tail节点的Next属性指向Head节点,就形成了所谓的“循环双向链表

当然,这样的结构可以在链表中再增加一个Tail节点属性,在做元素插入或删除时,可以循环到底以更新尾节点Tail(当然这样会给插入/删除元素带来一些额外的开销),但是却可以给GetItemAt(int i)方法带来优化的空间,比如要查找的元素在前半段时,可以从Head开始用next向后找;反之,如果要找的元素在后半段,则可以从Tail节点用prev属性向前找。

注:.Net中微软已经给出了一个内置的双向链表System.Collections.Generic.LinkedList,在了解双链表的原理后,建议大家直接系统内置的链表。

希望本文所述对大家C#程序设计有所帮助。

以上就是C#数据结构之双向链表(DbLinkList)实例详解的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » 其他教程