zoukankan      html  css  js  c++  java
  • 数据结构C#版笔记双向链表(DbLinkList)

    这是数据结构C#版笔记--线性表(Data Structure)之单链表(LinkList)的继续,对于双向链接,节点上除了Next属性外,还要有Prev属性用来指向前一个节点,DbNode定义如下:

    namespace 线性表
    {
        public class DbNode<T>
        {
            private T data;
            private DbNode<T> prev;
            private DbNode<T> next;
    
    
            public DbNode(T data, DbNode<T> next,DbNode<T> prev)
            {
                this.data = data;
                this.next = next;
                this.prev = prev;
            }
    
            public DbNode(T data, DbNode<T> next) 
            {
                this.data = data;
                this.next = next;
                this.prev = null;
            }
    
            public DbNode(DbNode<T> 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<T> Prev 
            {
                get { return prev; }
                set { prev = value; }
            }
    
            public DbNode<T> Next 
            {
                get { return next; }
                set { next = value; }
            }
    
    
        }
    }
    
    

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

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

    完整实现DbLinkList<T>:

    using System;
    using System.Text;
    
    namespace 线性表
    {
        public class DbLinkList<T> : IListDS<T>
        {
            private DbNode<T> head;
    
            public DbNode<T> Head
            {
                get { return head; }
                set { head = value; }
            }
    
            public DbLinkList()
            {
                head = null;
            }
    
            /// <summary>
            /// 类索引器
            /// </summary>
            /// <param name="index"></param>
            /// <returns></returns>
            public T this[int index] 
            {
                get 
                {
                    return this.GetItemAt(index);
                }
            }
    
            /// <summary>
            /// 返回单链表的长度
            /// </summary>
            /// <returns></returns>
            public int Count()
            {
                DbNode<T> p = head;
                int len = 0;
                while (p != null)
                {
                    len++;
                    p = p.Next;
                }
                return len;
            }
    
            /// <summary>
            /// 清空
            /// </summary>
            public void Clear()
            {
                head = null;
            }
    
            /// <summary>
            /// 是否为空
            /// </summary>
            /// <returns></returns>
            public bool IsEmpty()
            {
                return head == null;
            }
    
            /// <summary>
            /// 在最后附加元素
            /// </summary>
            /// <param name="item"></param>
            public void Append(T item)
            {
                DbNode<T> d = new DbNode<T>(item);
                DbNode<T> n = new DbNode<T>();
    
                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<T> q = new DbNode<T>(item);
                    q.Next = head;//把"头"改成第二个元素
                    head.Prev = q;
                    head = q;//把自己设置为"头"
                    return;
                }
    
                DbNode<T> n = head;
                DbNode<T> d = new DbNode<T>();
                int j = 0;
    
                //找到位置i的前一个元素d
                while (n.Next != null && j < i)
                {
                    d = n;
                    n = n.Next;
                    j++;
                }
    
                if (n.Next == null) //说明是在最后节点插入(即追加)
                {
                    DbNode<T> q = new DbNode<T>(item);
                    n.Next = q;
                    q.Prev = n;
                    q.Next = null;
                }
                else
                {
                    if (j == i)
                    {
                        DbNode<T> q = new DbNode<T>(item);
                        d.Next = q;
                        q.Prev = d;
                        q.Next = n;
                        n.Prev = q;
                    }
                }
            }
    
            /// <summary>
            /// 在位置i后插入元素item
            /// </summary>
            /// <param name="item"></param>
            /// <param name="i"></param>
            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<T> q = new DbNode<T>(item);
                    q.Next = head.Next;
                    head.Next.Prev = q;
                    head.Next = q;
                    q.Prev = head;
                    return;
                }
    
                DbNode<T> p = head;
                int j = 0;
    
                while (p != null && j < i)
                {
                    p = p.Next;
                    j++;
                }
                if (j == i)
                {
                    DbNode<T> q = new DbNode<T>(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!");
                }
            }
    
            /// <summary>
            /// 删除位置i的元素
            /// </summary>
            /// <param name="i"></param>
            /// <returns></returns>
            public T RemoveAt(int i)
            {
                if (IsEmpty() || i < 0)
                {
                    Console.WriteLine("Link is empty or Position is error!");
                    return default(T);
                }
    
                DbNode<T> q = new DbNode<T>();
                if (i == 0)
                {
                    q = head;
                    head = head.Next;
                    head.Prev = null;
                    return q.Data;
                }
    
                DbNode<T> p = head;
                int j = 0;
    
                while (p.Next != null && j < i)
                {
                    j++;
                    q = p;
                    p = p.Next;
                }
    
                if (j == i)
                {
                    p.Next.Prev = q;
                    q.Next = p.Next;                
                    return p.Data;
                }
                else
                {
                    Console.WriteLine("The node is not exist!");
                    return default(T);
                }
            }
    
            /// <summary>
            /// 获取指定位置的元素
            /// </summary>
            /// <param name="i"></param>
            /// <returns></returns>
            public T GetItemAt(int i)
            {
                if (IsEmpty())
                {
                    Console.WriteLine("List is empty!");
                    return default(T);
                }
    
                DbNode<T> p = new DbNode<T>();
                p = head;
    
                if (i == 0) 
                { 
                    return p.Data; 
                }
    
                int j = 0;
    
                while (p.Next != null && j < i)
                {
                    j++;
                    p = p.Next;
                }
    
                if (j == i)
                {
                    return p.Data;
                }
                else
                {
                    Console.WriteLine("The node is not exist!");
                    return default(T);
                }
            }
    
            //按元素值查找索引
            public int IndexOf(T value)
            {
                if (IsEmpty())
                {
                    Console.WriteLine("List is Empty!");
                    return -1;
                }
                DbNode<T> p = new DbNode<T>();
                p = head;
                int i = 0;
                while (!p.Data.Equals(value) && p.Next != null)
                {
                    p = p.Next;
                    i++;
                }
                return i;
            }
    
            /// <summary>
            /// 元素反转
            /// </summary>
            public void Reverse()
            {
                DbLinkList<T> result = new DbLinkList<T>();
                DbNode<T> t = this.head;
                result.Head = new DbNode<T>(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<T> GetNodeAt(int i){
                if (IsEmpty())
                {
                    Console.WriteLine("List is empty!");
                    return null;
                }
    
                DbNode<T> p = new DbNode<T>();
                p = head;
    
                if (i == 0)
                {
                    return p;
                }
    
                int j = 0;
    
                while (p.Next != null && j < i)
                {
                    j++;
                    p = p.Next;
                }
    
                if (j == i)
                {
                    return p;
                }
                else
                {
                    Console.WriteLine("The node is not exist!");
                    return null;
                }
            }
    
            /// <summary>
            /// 测试用prev属性从后面开始遍历
            /// </summary>
            /// <returns></returns>
            public string TestPrevErgodic() 
            {
                DbNode<T> 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<T> 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<string> dblink = new DbLinkList<string>();
                dblink.Head = new DbNode<string>("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<T>,在了解双链表的原理后,建议大家直接系统内置的链表。

    作者:菩提树下的杨过
    出处:http://yjmyzz.cnblogs.com
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    mysql 历史版本下载
    mysql 5.7 版本 You must reset your password using ALTER USER statement before executing this statement报错处理
    5.7 zip 版本的安装 以及遇到的坑
    mysql 5.6zip版本的卸载与5.7 zip 版本的安装
    mysql数据库的备份与还原
    本地Navicat连接docker里的mysql
    docker修改数据库密码
    docker 在push镜像到本地registry出现的500 Internal Server Error
    linux 没有界面内容显示不全解决办法
    json与map互相转换
  • 原文地址:https://www.cnblogs.com/yjmyzz/p/1859689.html
Copyright © 2011-2022 走看看