链表思想(我是如何理解链表)

  链表是一种重要的数据结构,是一种数据的存储方式。链表由多个链表元素组成,每个元素称为节点。链表存储的物理结构可能是连续的,但也可能是无序的。但是链表之间的元素(节点)是有序的逻辑相连。

  链表分为:单(向)链表、循环链表、双向链表。

  虽然有三种不同的链表,但是其中心思想(存储的逻辑结构)是一样的。笔者以单链表来分享其思想。

  首先来看链表的节点是怎么定义的:代码如下

    class Node<T>                                           //定义为泛型,根据用户存储需求决定存储的数据类型
    {
        public T Date { get; set; }                         //存储的数据
        public Node<T> NodeFront { get; set; }              //前驱
        public Node<T> NodeNext { get; set; }               //后继

        public Node(T t)                                    //构造函数
        {
            Date = t;
            NodeFront = NodeNext = null;                    //刚开始初始化的节点没有前驱和后继
        }
        public Node()                                       //无参构造函数
        {
            Date = default(T);                              //泛型默认的初始化方式
            NodeFront = NodeNext = null;
        }
    }

  节点类有三个属性成员,分别是Date、NodeFront、NodeNext。其中NodeFront、NodeNext的类型也是Node<T>的引用类型,也就相当于NodeFront、NodeNext也是属于节点的。由于是单向链表,我们只用到后继NodeNext

  比如我们创建三个节点:node1、node2、node3,然后输出note2的值,代码如下:

        static void Main(string[] args)
        {
            Node<int> node1 = new Node<int>(1);
            Node<int> node2 = new Node<int>(2);
            Node<int> node3 = new Node<int>(3);

            node1.NodeNext = node2;
            node2.NodeNext = node3;

            //输出node2的值
            Console.WriteLine(node2.Date);
            Console.WriteLine(node1.NodeNext.Date);

            Console.ReadKey();
        }

  结果是输出的值都是2。

  所以说NodeNext也是属于节点的,它就相当于node2节点。但是,我们在用单向链表的时候只有头节点和尾节点,那么NodeNext就很有用了。只要我们知道头节点和下标,就可以一步一步算出指定位置的值。

  画个图帮助理解下:

《链表思想(我是如何理解链表)》

  单向链表存储的逻辑结构其实有点像嵌套。node1的NodeNext成员保存了node2,node2的NodeNext成员又保存了node3,node3的NodeNext成员又保存了下一个节点直到尾节点的NodeNext保存的是null。

 

  清楚了单向链表,实现其功能就很简单了,用来用去就是用到NodeNext这个成员。

  先创建一个MyList类,代码如下:

    class MyList<T>
    {     
        private int listSize = 0;
        private Node<T> ListHead;           //存储链表的头
        private Node<T> ListTail;           //存储链表的尾

        //为listSize添加一个只读属性
        public int ListSize
        {
            get
            {
                return listSize;
            }
        }
    }

  笔者在写添加一个listSize只读属性的时候当时是这样写的:

        public int ListSize { get; }

  Tip:当时想把ListSize即当字段用,又当属性用。但是发现,在类里面,字段ListSize也是不可写入的。所以在写只读属性时,字段和属性还是分开写好。

  接下来,笔者分享其中一个功能的实现思想(有点懒= =),其实也算是最复杂的一个功能,会了这个其他都不是事儿了:在指定位置插入指定的元素

代码如下:

        //向链表插入指定位置插入一个元素(前插)
        public bool Insert_Front(T date, uint position)
        {
            if (IsEmpty())                                  //链表为空
                return false;
            else if (position > listSize)                   //超出链表长度或则为链表尾巴
                return false;
            if (position == 1)                              //在链表头插入
            {
                Node<T> newNode = new Node<T>(date);        //创建一个节点
                newNode.NodeNext = ListHead;                //新节点的后继指向头节点
                ListHead = newNode;                         //新节点变成头节点
                ++listSize;
                return true;
            }
            else
            {
                Node<T> tempNode = ListHead;                //创建一个临时节点保存链表头
                Node<T> tempFrontNode = new Node<T>();      //创建一个节点,保存tempNode的上一个节点
                uint ui = 1;
                while (ui < position && tempNode.NodeNext != null)
                {
                    tempFrontNode = tempNode;               //保存tempNode的上一个节点tempFrontNode(前插的时候会用到这个节点)
                    tempNode = tempNode.NodeNext;           //tempNode自身变成了下一个节点
                    ui++;
                }
                Node<T> newNode = new Node<T>(date);        //创建新节点
                tempFrontNode.NodeNext = newNode;           //tempFrontNode的后继等于新节点
                newNode.NodeNext = tempNode;                //新节点的后继节点等于tempNode节点
                ++listSize;                                 //链表长度加1
                return true;
            }
        }

画张图来脑补一下:(不表示在头节点前面插入

《链表思想(我是如何理解链表)》

  只要理解了节点链接的逻辑,所有的功能都会很好实现的。下面贴一下完整的代码:

《链表思想(我是如何理解链表)》
《链表思想(我是如何理解链表)》

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

namespace 线性链表
{


    class Node<T>                                           //定义为泛型,根据用户存储需求决定存储的数据类型
    {
        public T Date { get; set; }                         //存储的数据
        public Node<T> NodeFront { get; set; }              //前驱
        public Node<T> NodeNext { get; set; }               //后继

        public Node(T t)                                    //构造函数
        {
            Date = t;
            NodeFront = NodeNext = null;                    //刚开始初始化的节点没有前驱和后继
        }
        public Node()                                       //无参构造函数
        {
            Date = default(T);                              //泛型默认的初始化方式
            NodeFront = NodeNext = null;
        }
    }
    class MyList<T>
    {
        private int listSize = 0;
        private Node<T> ListHead;           //存储链表的头
        private Node<T> ListTail;           //存储链表的尾

        //为listSize添加一个只读属性
        public int ListSize
        {
            get
            {
                return listSize;
            }
        }


        //判断线性链表是否为空
        public bool IsEmpty()
        {
            return ListHead == null ? true : false;
        }

        //向链表里添加一个元素
        public void Push_In(T date)
        {
            Node<T> newNode = new Node<T>(date);        //创建一个节点

            if (IsEmpty())                          //链表是否为空            
                ListHead = newNode;                 //链表为空时,添加的元素为头节点
            else
                ListTail.NodeNext = newNode;        //否者原先的尾节点的NodeNext成员(后继)指向新元素。
            ListTail = newNode;                     //新元素变成尾节点
            ++listSize;
        }

        //向链表插入指定位置插入一个元素(前插)
        public bool Insert_Front(T date, uint position)
        {
            if (IsEmpty())                                  //链表为空
                return false;
            else if (position > listSize)                   //超出链表长度或则为链表尾巴
                return false;
            if (position == 1)                              //在链表头插入
            {
                Node<T> newNode = new Node<T>(date);        //创建一个节点
                newNode.NodeNext = ListHead;                //新节点的后继指向头节点
                ListHead = newNode;                         //新节点变成头节点
                ++listSize;
                return true;
            }
            else
            {
                Node<T> tempNode = ListHead;                //创建一个临时节点保存链表头
                Node<T> tempFrontNode = new Node<T>();      //创建一个节点,保存tempNode的上一个节点
                uint ui = 1;
                while (ui < position && tempNode.NodeNext != null)
                {
                    tempFrontNode = tempNode;               //保存tempNode的上一个节点tempFrontNode(前插的时候会用到这个节点)
                    tempNode = tempNode.NodeNext;           //tempNode自身变成了下一个节点
                    ui++;
                }
                Node<T> newNode = new Node<T>(date);        //创建新节点
                tempFrontNode.NodeNext = newNode;           //tempFrontNode的后继等于新节点
                newNode.NodeNext = tempNode;                //新节点的后继节点等于tempNode节点
                ++listSize;                                 //链表长度加1
                return true;
            }
        }

        //向链表插入指定位置插入一个元素(后插)
        public void Insert_Back(T date, uint position)
        {
            if (IsEmpty())
                Console.WriteLine("链表为空");
            else if (position > listSize)
                Console.WriteLine("越界了");
            else
            {
                Node<T> newNode = new Node<T>(date);
                Node<T> tempNode = ListHead;
                Node<T> tempFrontNode = new Node<T>();
                uint ui = 1;
                if (position == listSize)                   //如果在表尾
                {
                    ListTail.NodeNext = newNode;
                    ListTail = newNode;
                }
                else
                {
                    while (ui <= position && tempNode.NodeNext != null)
                    {
                        tempFrontNode = tempNode;
                        tempNode = tempNode.NodeNext;
                        ui++;
                    }
                    tempFrontNode.NodeNext = newNode;
                    newNode.NodeNext = tempNode;
                }
                ++listSize;
            }

        }

        //在链表删除一个指定元素
        public bool Delete_Out(T date)
        {
            if (IsEmpty())
            {
                Console.WriteLine("列表为空,无法删除");
                return false;
            }
            else
            {
                Node<T> tempNode = ListHead;                    //创建临时节点,保存表头
                Node<T> tempFrontNode = new Node<T>();          //保存临时节点的前驱
                Node<T> tempNextNode = new Node<T>();           //保存临时节点的后驱
                uint ui = 1;
                while (!tempNode.Date.Equals(date) && ui <= listSize)
                {
                    tempFrontNode = tempNode;
                    if (tempNode.NodeNext != null)                  //如果不为表尾
                    {
                        tempNode = tempNode.NodeNext;
                        tempNextNode = tempNode.NodeNext;
                    }

                    ui++;
                }//end while
                if (ui > listSize)
                {
                    Console.WriteLine("没有找到指定元素");
                    return false;
                }
                else
                {
                    if (ui == listSize)                           //删除表尾
                        ListTail = tempFrontNode;
                    else
                    {
                        tempFrontNode.NodeNext = tempNextNode;
                        tempNextNode.NodeFront = tempFrontNode;
                    }
                    --listSize;
                    GC.Collect();               //强制垃圾回首
                    return true;
                }
            }//end else           
        }
        //输出列表的元素
        public void Print_Element()
        {
            if (IsEmpty())
            {
                Console.WriteLine("列表为空");
            }
            else
            {
                Node<T> tempNode = ListHead;
                uint i = 1;
                Console.Write("元素为:");
                do
                {
                    Console.Write("{0} ", tempNode.Date);
                    tempNode = tempNode.NodeNext;
                    i++;
                } while (i <= listSize);
                Console.WriteLine();
            }

        }

        //查找指定位置的元素
        public T GetElement(uint index)
        {
            T item = default(T);                //默认初始化泛型
            if (IsEmpty())
                Console.WriteLine("链表为空");
            else if (index > listSize)
                Console.WriteLine("越界了");
            else
            {
                uint uCount = 1;
                Node<T> tempNode = ListHead;
                while (uCount != index && tempNode.NodeNext != null)
                {
                    tempNode = tempNode.NodeNext;
                    uCount++;
                }
                item = tempNode.Date;

            }


            return item;
        }
    }


}

View Code

 

 

  

 

    原文作者:小洋大海
    原文地址: https://www.cnblogs.com/syman/p/6297993.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞