静态链表及C#实现

静态链表

静态链表是用数组模拟动态链表。

静态链表结构描述

首先,静态链表使用数组来模拟动态链表。
数组存放一个节点对象,对象包括数据与和游标域两个属性。
在这个数组中,第一个元素用来指示第一个可用节点的位置,最后一个元素用来指示第一个包含元素的节点(相当于头节点)。
也可以理解为,数组中存放了两个节点,一个是备用节点(存放数组中的空位置),头节点的下表是[0];另一个是已用节点(存放数组中包含元素的位置),头节点的下表是[数组长度-1]。

数组的第一个元素,即下标为0的元素的cur就存放备用链表的第一个结点的下标;而数组的最后一个元素的cur则存放第一个有数值的元素的下标,相当于单链表的头节点作用,当整个链表为空时,则为0,表示无指向。如图3-12-2所示


现在如果我们需要在“乙”和“丁”之间插入一个值为“丙”的元素,只需要将“乙”的cur改为7,表示下一位是“丙”,并将“丙”的cur改为3,表示下一位是丁。如图3-12-3所示。


现在如果我们删除了第一个元素“甲”,表示现在“甲”这个位置空出来了,如果未来有新人要来则优先考虑这里,所以删除的位置成为第一个优先空位,即首元素的cur为1, 第一个元素位置的cur改为8,而下标为8的位置cur改为9,最后元素位置的cur改为2,如图3-12-4所示。


代码实现

下面的实现代码中,可用链表(备用链表)的头节点的Next域为已用链表的头节点时(及数组首元素.Next=尾元素的下标)即为可用链表已满。因为在代码实现中,初始化时每个节点的.Next域都是他的下一个元素的下标,所以当遍历到可用链表的最后一个链表元素时,他的.Next域就是最后一个数组元素。

Node类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace test2
{
    class Node
    {
        T data; //数据
        int next;   //下一个的游标

        public T Data { get => data; set => data = value; }
        public int Next { get => next; set => next = value; }

        //构造器
        public Node()
        {
            Data = default(T);
            Next = 0;
        }
    }
}

StaticLink类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace test2
{
    class StaticLink
    {
        Node[] lst;
        int length = 0;

        public int Length { get => length; set => length = value; }
        internal Node[] Lst { get => lst; set => lst = value; }

        /// 
        /// 构造器
        /// 
        /// 
        public StaticLink(int len)
        {
            len = len + 2;
            Lst = new Node[len]; //数组第一个和最后一个不能存数据,所以补上两个
            for (int j = 0; j < len; j++)
            {
                Lst[j] = new Node();
                Lst[j].Next = j + 1;
            }
            Lst[len - 1].Next = 0;
        }

        /// 
        /// 申请一个节点,获取第一个可用节点位置
        /// 
        /// 
        int malloc()
        {
            int i = Lst[0].Next;    //获取备用头节点中存放的下一个可用节点位置。

            //当申请的节点位置和最后一个节点位置相同时,说明可用链表已经分配完毕。
            if (i != Lst.Length - 1)
            {
                ++Length;//更新链表长度

                return i;
            }
            else { Console.WriteLine("链表已满"); return 0; }
        }

        /// 
        ///归还一个节点到备用链表上
        /// 
        /// 要删除的节点的前驱位置
        void free(int pos)
        {
            int c = Lst[pos].Next;  //要删除的节点
            
            //删除首节点
            if (pos == 0)
            {
                Lst[Lst.Length - 1].Next = Lst[1].Next;
                Lst[1].Next = Lst[0].Next;    //更新可用链表 
                Lst[0].Next = 1;  //更新可用链表头节点
            }
            //删除节点
            else
                Lst[pos].Next = Lst[c].Next;

            --Length;
        }

        /// 
        /// 在链表尾部添加一个元素
        /// 
        /// 
        public void Append(T value)
        {
            int pos = malloc();   //获取第一个可用节点位置

            //如果节点可用
            if (pos > 0)
            {
                //遍历已用链表,挂载新节点
                int k = Lst[Lst.Length - 1].Next;
                while (Lst[k].Next != 0)
                {
                    k = Lst[k].Next;
                }
                Lst[k].Next = pos;

                Lst[0].Next = Lst[pos].Next;//更新备用链表头节点
                Lst[pos].Next = 0;    //更新已用链表结尾
                //添加节点数据
                Lst[pos].Data = value;

                //仅在第一次附加时更新已用链表的头节点
                if (Length == 0)
                {
                    //Console.WriteLine("已用"+i);
                    Lst[Lst.Length - 1].Next = pos;//更新已用链表头节点
                }
            }
        }

        /// 
        /// 在指定位置后插入一个元素
        /// 
        /// 插入元素的值
        /// 插入位置
        public void Insert(T value, int i)
        {
            if (i > Length)
            {
                Console.WriteLine("位置不正确或链表为空");
                return;
            }

            int k = malloc();
            if (k > 0)
            {
                Lst[k].Data = value; //在新申请的节点中填充数据

                int j = Lst[Lst.Length-1].Next;
                //遍历查找位置
                for (int n = 0; n < i - 1; n++)
                {
                    j = Lst[j].Next;
                }

                Lst[0].Next = Lst[k].Next;  //更新可用链表首节点
                Lst[k].Next = Lst[j].Next; ;    //更新已用链表结尾
                Lst[j].Next = k; //挂载节点
            }

        }

        /// 
        /// 删除指定位置的元素
        /// 
        /// 元素在链表中的位置
        public void Delete(int pos)
        {
            //pos = pos + 1;
            if (pos > Length)
            {
                Console.WriteLine("位置不正确或链表为空");
                return;
            }

            //删除首节点
            if (pos == 1)
            {
                free(0);
                return;
            }


            //遍历要删除节点的前驱
            int k = Lst[Lst.Length - 1].Next;
            Console.WriteLine("k1:" +k);
            for (int i = 0; i < pos - 2; i++)
            {
                k = Lst[k].Next;
            }

            Console.WriteLine("k:" + k);
            free(k);
        }
    }
}

Program类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace test2
{
    class Program
    {
        static void Main(string[] args)
        {
            StaticLink link = new StaticLink(5);
            link.Append("123");
            link.Append("456");
            link.Append("789");
            //link.Insert("asd", 2);

            link.Delete(1);

            Console.WriteLine("---数组打印");
            for (int j = 0; j < link.Lst.Length; j++)
            {
                Console.WriteLine(j + ":" + link.Lst[j].Data + ":" + link.Lst[j].Next);
            }

            Console.WriteLine("---链表打印");
            int i = link.Lst[link.Lst.Length - 1].Next;
            while (i != 0)
            {
                Console.WriteLine(link.Lst[i].Data);
                i = link.Lst[i].Next;
            }
        }
    }
}

你可能感兴趣的:(静态链表及C#实现)