数据结构(2):链表

本文详细介绍了如何在C#中实现链表数据结构,包括使用引用和指针两种方式,并探讨了静态链表的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在上一篇文章中,实现线性表中的顺序表,对于插入、删除操作比较频繁的数据来说,用顺序表就不是很经济了,这时候链表就排上用场
在C#中,直接使用指针来进行操作是比较麻烦的,不过引用可以在相当程度上代替指针,这也是C#中实现链表最简单的方法。
首先,使用引用来实现链表,通过两个类来实现的,一个事节点类,一个是链表类。主要实现了向末尾添加节点、指定位置插入节点、删除节点这些操作。

  class RefNode<T>  //节点类
    {
        public T data;
        public RefNode<T> Next;        

        public RefNode()
        {
            data = default(T);
            Next = null;
        }
        public RefNode(T dt)
        {
            data = dt;
            Next = null;
        }
    }
     class RefLinkList<T>
    {
        RefNode<T> HeadNode;
        public int Length;
        public RefLinkList()
        {
            HeadNode = new RefNode<T>();
        }

        public void Add(RefNode<T> data)    //在末尾添加节点
        {
            Length++;          
            if (HeadNode.Next == null)          //若链表为空
            {
                HeadNode.Next = new RefNode<T>();
                HeadNode.Next = data;         
            }
            else
            {
                var p = HeadNode.Next;
                while (p.Next != null)
                {
                    p = p.Next;
                }
                p.Next = new RefNode<T>();
                p.Next = data;
            }
        }
        public bool Get(int index, out RefNode<T> tp)  //获取指定序号的节点
        {
            if(index > Length || index < 0)                         //若序号超出范围
            {
                Console.WriteLine("序号超出范围!");
                tp = null;
                return false;
            }
            var p = HeadNode;
            for(int i = 0; i < index; i++)
            {
                p = p.Next;
            }
            tp = p;
            return true;
        }
        public bool Insert(int index, RefNode<T> node)//在指定位置插入节点
        {
            if(index > Length || index <= 0)
            {
                Console.WriteLine("索引超出范围");
                return false;
            }
            var current = new RefNode<T>();
            Get(index - 1, out current);
            var temp = current.Next;
            current.Next = node;
            node.Next = new RefNode<T>();
            node.Next = temp;
            Length++;
            return true;
        }
        public bool Delete(int index) //删除指定位置的节点
        {          
            var p = new RefNode<T>();

            if (Get(index - 1, out p))
            {
                var tp = new RefNode<T>();
                Get(index, out tp);

                if (tp.Next == null)
                    p.Next = null;
                else
                    p.Next = tp.Next;

                Length--;
                return true;
            }
            else
                return false;

        }

        public void Show()//显示链表
        {
            var p = HeadNode.Next;
            Console.WriteLine("There are {0} elements in this  linklist.");
            if(p == null)
            {
                Console.WriteLine("This Link list is a void list;");
            }
            while(p != null)
            {
                Console.WriteLine(p.data);
                p = p.Next;
            }
        }

    }

下面我们使用指针来实现。使用指针主要注意两点,一是使用指针的地方要用unsafe进行标识;二是指针不能指向不能确定内存大小的泛型类型。为了使指针实现的链表具有通用性,需要使用using来创建别名。即:

using ElemType = System.Int32;  //这里可以根据需要进行更改
struct PtrNode  //通过指针实现的链表
    {
        ElemType data;
        unsafe PtrNode* Next;       
    }

其余操作与引用是相似的,这里就不多说了。
最后,来讲讲静态链表。静态链表是没有指针的高级语言,为了获得链表的便利而想出的实现方法。这种方法有两个数据域,一个用于存储数据,一个用于描述下个节点的位置。描述节点位置的数据称为游标,这种方法又称为游标实现法。这里有两个特殊节点,第一个节点和最后一个节点(注意:不是链表的尾节点),这两个节点不存储数据。第一个节点的游标描述备用空间的第一个节点的位置,最后一个节点的游标存储链表第一个节点的位置。链表的尾节点的游标为0。具体实现如下:

class StaticNode<T> //静态链表节点
    {
        public T data;
        public int cur;
        public bool IsEnd;
        public StaticNode()
        {
            data = default(T);
            cur = 0;
            IsEnd = false;
        }
    }
 class StaticLinkList<T>
    {
        const int MaxSize = 1000;
        private int length;
        public StaticNode<T>[] Data;

        public StaticLinkList()
        {
            Data = new StaticNode<T>[MaxSize];
            for (int i = 0; i < MaxSize; i++)
                Data[i] = new StaticNode<T>();
            for (int i = 0; i < MaxSize - 1; i++)
                Data[i].cur = i + 1;
            Data[MaxSize -1 ].cur = 0;                  //目前静态链表为空
            length = 0;
        }

        public int GetLocation(int index)  //得到第index个元素的位置
        {
            if (index > length || index <= 0)//序号超出范围或序号为零
            {
                Console.WriteLine("序号为零或序号大于链表长度!");
                return 0;
            }
            int i = Data[MaxSize - 1].cur;
            int count = 1;
            while(count < index)
            {
                count++;
                i = Data[i].cur;
            }
            return i;
        }

        public bool Add(T Elem)   //向表尾添加数据
        {
            if (Data[0].cur == MaxSize - 1)  //若链表已满
                return false;
            if (Data[MaxSize - 1].cur == 0)  //若插入的是第一个节点,则更新第一个节点的值
                Data[MaxSize - 1].cur = 1;
            int i = Data[0].cur;
            int prior = 0;          
            Data[i].data = Elem;       
            Data[0].cur = Data[i].cur;
            if (length >= 1)
            {
                prior = GetLocation(length);
                Data[prior].cur = i;
            }
            Data[i].cur = 0;
            length++;
            return true;
        }
        public bool Insert(T Elem, int index) //在第index个位置上插入
        {
            if(index <= 0 || index > length)
            {
                Console.WriteLine("索引超出范围!");
                return false;
            }
            if (length >= MaxSize - 2)
            {
                Console.WriteLine("链表已满!");
                return false;
            }
            int tp = GetLocation(index);
            int j = Data[0].cur;
            Data[0].cur = Data[j].cur;
            Data[j].data = Elem;
            Data[j].cur = tp;
            if(index != 1)                   //若插入位置不是第一个节点
            {
                int prior = GetLocation(index - 1);
                Data[prior].cur = j;
            }
            else
            {
                Data[MaxSize - 1].cur = j;           //插入位置是第一个节点
            }
            length++;
            return true;

        }
        public bool Delete(int index) //删除第index个元素
        {
            if(index > length || index <= 0)
            {
                Console.WriteLine("索引超出范围!");
                return false;
            }
            if(index == 1)   //若删除的是第一个节点
            {
                int j = Data[MaxSize - 1].cur;
                Data[MaxSize - 1].cur = Data[j].cur;
                Data[j].cur = Data[0].cur;
                Data[0].cur = j;
                length--;
                return true;
            }
            else if(index == length) //若删除的是尾节点
            {
                int prior = GetLocation(index - 1);
                int tp = Data[prior].cur;
                Data[tp].cur = Data[0].cur;
                Data[0].cur = tp;
                Data[prior].cur = 0;
                length--;
                return true;
            }
            else
            {
                int prior = GetLocation(index - 1);
                int next = GetLocation(index + 1);
                int tp = Data[prior].cur;
                Data[tp].cur = Data[0].cur;
                Data[0].cur = tp;
                Data[prior].cur = next;
                length--;
                return true;
            }
        }

        public int Length()
        {
            return length;
        }

        public void Show()
        {
            Console.WriteLine("This list have {0} elements.", Length());
            int tp = Data[MaxSize - 1].cur;
            while(tp != 0)
            {
                Console.WriteLine(Data[tp].data);
                tp = Data[tp].cur;
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值