顺序表是用地址连续的存储单元顺序存储线性表中的各个数据元素, 逻辑上相邻的数据元素在物理位置上也相邻。因此,在顺序表中查找任何一个位置上的数据元素非常方便, 这是顺序存储的优点。 但是, 在对顺序表进行插入和删除时,需要通过移动数据元素来实现,影响了运行效率。线性表的另外一种存储结构——链式存储(Linked Storage), 这样的线性表叫链表(Linked List)。 链表不要求逻辑上相邻的数据元素在物理存储位置上也相邻,因此,在对链表进行插入和删除时不需要移动数据元素,但同时也失去了顺序表可随机存储的优点。

  把存储据元素本身信息的域叫结点的数据域(Data Domain), 把存储与它相邻的数据元素的存储地址信息的域叫结点的引用域(Reference Domain)。如果结点的引用域只存储该结点直接后继结点的存储地址, 则该链表叫单链表(Singly Linked List)。把该引用域叫 next。把单链表结点看作是一个类,类名为 Node<T>。

  单链表结点类的实现如下:

//节点类
    public class Node<T>
    {
        private T data;//存储数据域
        private Node<T> next;//存储引用域

        public Node(T val, Node<T> node)
        {
            data = val;
            next = node;
        }
        public Node(T val)
        {
            data = val;
            next = null;
        }
        public Node(Node<T> node)
        {
            next = node;
        }
        public Node()
        {
            data = default(T);
            next = null;
        }
        public T Data
        {
            get { return data; }
            set { data = value; }
        }
        public Node<T> Next
        {
            get { return next; }
            set { next = value; }
        }
    }

  

  单链表的接口实现如下:

public interface IListDS<T>//单链表的接口
    {
        int GetLength(); //求长度
        void Clear(); //清空操作
        bool IsEmpty();
        void Append(T item); //附加操作
        void Insert(T item, int i); //插入操作
        T Delete(int i); //删除操作
        T GetElem(int i); //取表元
        int Locate(T value); //按值查找
        void Reverse();//倒置
    }

  

  单链表如图所示:

由图可知,单链表由头引用 H 唯一确定。头引用指向单链表的第一个结点,也就是把单链表第一个结点的地址放在 H 中,所以,H 是一个 Node 类型的变量。头引用为 null 表示一个空表。把单链表看作是一个类,类名叫 LinkList<T>。LinkList<T>类也实现了接口IListDS<T>。LinkList<T>类有一个字段 head,表示单链表的头引用,所以 head的类型为 Node<T>。由于链表的存储空间不是连续的,所以没有最大空间的限制,在链表中插入结点时不需要判断链表是否已满。

  单链表类 LinkList<T>的实现说明如下所示:

public class LinkList<T> : IListDS<T>//单链表类
    {
        private Node<T> head;

        public Node<T> Head
        {
            get { return head; }
            set { head = value; }
        }
        public LinkList()
        {
            head = null;
        }
        public int GetLength()
        {
            int i = 0;
            while (head != null)
            {
                head = head.Next;
                ++i;
            }
            return i;
        }

        public void Clear()
        {
            head = null;
        }

        public bool IsEmpty()
        {
            return head == null;
        }

        public void Append(T item)//在单链表的末尾添加新元素
        {
            Node<T> q = new Node<T>(item);
            Node<T> p = new Node<T>();
            if (head == null)
            {
                head = q;
                return;
            }
            p = head;
            while (p.Next != null)
            {
                p = p.Next;
            }
            p.Next = q;
        }
        //在单链表的第i个结点的位置前插入一个值为item的结点
        public void Insert(T item, int i)
        {
            if (IsEmpty() || i < 1)
            {
                Console.WriteLine(" List is empty or Position is error! ");
                return;
            }
            if (i == 1)
            {
                Node<T> q = new Node<T>(item);
                q.Next = head;
                head = q;
                return;
            }
            Node<T> p = head;
            Node<T> r = new Node<T>();
            int j = 0;
            while (p.Next != null && j < i)
            {
                ++j;
                r = p;
                p = p.Next;
            }
            if (i == j)
            {
                Node<T> q = new Node<T>(item);
                q.Next = p;
                r.Next = q;
            }
        }
        //在单链表的第i个结点的位置后插入一个值为item的结点
        public void InsertPost(T item, int i)
        {
            if (IsEmpty() || i < 1)
            {
                Console.WriteLine(" List is empty or Position is error! ");
                return;
            }
            Node<T> p = head;
            int j = 0;
            while (p.Next != null && j < i)
            {
                p = p.Next;
                ++j;
            }
            if (i == j)
            {
                Node<T> q = new Node<T>(item);
                q.Next = p.Next;
                p.Next = q;
            }
        }
        public T Delete(int i)
        {
            if (IsEmpty() || i < 1)
            {
                Console.WriteLine(" List is empty or Position is error! ");
                return default(T);
            }
            Node<T> p = head;
            Node<T> q = new Node<T>();
            int j = 0;
            while (p.Next != null && j < i)
            {
                q = p;
                p = p.Next;
                ++j;
            }
            if (i == j)
            {
                q.Next = p.Next;
                return p.Data;
            }
            else
            {
                Console.WriteLine("The ith node is not exist!");
                return default(T);
            }

        }

        public T GetElem(int i)
        {
            if (IsEmpty() || i < 1)
            {
                Console.WriteLine(" List is empty or Position is error! ");
                return default(T);
            }
            Node<T> p = head;
            int j = 0;
            while (p.Next != null && j < i)
            {
                p = p.Next;
                ++j;
            }
            if (i == j)
            {
                return p.Data;
            }
            else
            {
                Console.WriteLine("The ith node is not exist!");
                return default(T);
            }
        }

        public int Locate(T value)
        {
            if (IsEmpty())
            {
                Console.WriteLine("List is Empty!");
                return -1;
            }
            Node<T> p = head;
            int i = 0;
            while (!p.Data.Equals(value) && p.Next != null)
            {
                p = p.Next;
                ++i;
            }
            return i;
        }

        public void Reverse()
        {
            Node<T> p = head.Next;
            Node<T> q = new Node<T>();
            head.Next = null;
            while (p != null)
            {
                q = p;
                p = p.Next;
                q.Next = head.Next;
                head.Next = q;
            }
        }
        //单链表的建立
        LinkList<int> CreateListHead()//头部插入结点建立单链表
        {
            LinkList<int> L = new LinkList<int>();
            int d = Int32.Parse(Console.ReadLine());
            while (d != -1)
            {
                Node<int> p = new Node<int>(d);
                p.Next = L.Head;
                L.Head = p;
                d = Int32.Parse(Console.ReadLine());
            }
            return L;
        }
        LinkList<int> CreateListTail()//在尾部插入结点建立单链表
        {
            LinkList<int> L = new LinkList<int>();
            Node<int> R = L.Head;
            int d = Int32.Parse(Console.ReadLine());
            while (d != -1)
            {
                Node<int> p = new Node<int>(d);
                if (L.Head == null)
                {
                    L.Head = p;
                }
                else
                {
                    R.Next = p;
                }
                R = p;
                d = Int32.Parse(Console.ReadLine());
            }
            if (R != null)
            {
                R.Next = null;
            }
            return L;
        }
    }

  

  但是有一个问题:前面介绍的单链表允许从一个结点直接访问它的后继结点,所以, 找直接后继结点的时间复杂度是 O(1)。但是,要找某个结点的直接前驱结点,只能从表的头引用开始遍历各结点。如果某个结点的 Next 等于该结点,那么,这个结点就是该结点的直接前驱结点。也就是说,找直接前驱结点的时间复杂度是 O(n),n是单链表的长度。当然,我们也可以在结点的引用域中保存直接前驱结点的地址而不是直接后继结点的地址。这样,找直接前驱结点的时间复杂度只有 O(1),但找直接后继结点的时间复杂度是 O(n)。如果希望找直接前驱结点和直接后继结点的时间复杂度都是 O(1),那么,需要在结点中设两个引用域,一个保存直接前驱结点的地址,叫 prev,一个直接后继结点的地址,叫 next,这样的链表就是双向链表(Doubly Linked List)。此引出了双链表。

C#单链表的更多相关文章

  1. 时间复杂度分别为 O(n)和 O(1)的删除单链表结点的方法

    有一个单链表,提供了头指针和一个结点指针,设计一个函数,在 O(1)时间内删除该结点指针指向的结点. 众所周知,链表无法随机存储,只能从头到尾去遍历整个链表,遇到目标节点之后删除之,这是最常规的思路和 ...

  2. 单链表的C++实现(采用模板类)

    采用模板类实现的好处是,不用拘泥于特定的数据类型.就像活字印刷术,制定好模板,就可以批量印刷,比手抄要强多少倍! 此处不具体介绍泛型编程,还是着重叙述链表的定义和相关操作.  链表结构定义 定义单链表 ...

  3. Java实现单链表的各种操作

    Java实现单链表的各种操作 主要内容:1.单链表的基本操作 2.删除重复数据 3.找到倒数第k个元素   4.实现链表的反转   5.从尾到头输出链表 6.找到中间节点 7.检测链表是否有环 8.在 ...

  4. [LeetCode] Linked List Cycle II 单链表中的环之二

    Given a linked list, return the node where the cycle begins. If there is no cycle, return null. Foll ...

  5. c++单链表基本功能

    head_LinkNode.h /*单链表类的头文件*/#include<assert.h>#include"compare.h"typedef int status; ...

  6. 单链表、循环链表的JS实现

    数据结构系列前言: 数据结构作为程序员的基本知识,需要我们每个人牢牢掌握.近期我也展开了对数据结构的二次学习,来弥补当年挖的坑......   当时上课的时候也就是跟着听课,没有亲自实现任何一种数据结 ...

  7. C代码实现非循环单链表

    C代码实现非循环单链表, 直接上代码. # include <stdio.h> # include <stdlib.h> # include <malloc.h> ...

  8. 分离的思想结合单链表实现级联组件:CascadeView

    本文介绍自己最近做省市级联的类似的级联功能的实现思路,为了尽可能地做到职责分离跟表现与行为分离,这个功能拆分成了2个组件并用到了单链表来实现关键的级联逻辑,下一段有演示效果的gif图.虽然这是个很常见 ...

  9. 数据结构:单链表结构字符串(python版)添加了三个新功能

    #!/urs/bin/env python # -*- coding:utf-8 -*- #异常类 class stringTypeError(TypeError): pass #节点类 class ...

  10. 数据结构:单链表结构字符串(python版)改进

    此篇文章的replace实现了字符串类的多次匹配,但依然有些不足. 因为python字符串对象为不变对象,所以replace方法并不修改原先的字符串,而是返回修改后的字符串. 而此字符串对象时用单链表 ...

随机推荐

  1. 我需要在Web上完成一个图片上传的功能

    我需要在Web上完成一个图片上传的功能. 这个页面需要能从手机中选择图片上传. 首先,这个页面是从微信上面触发的,所以修改了微信的的入口地址,增加了身份识别号作为传参. 跳转到页面的时候,页面先检查身 ...

  2. 解决Strokeit在win8下的图标问题和开机启动问题

    Strokeit目前和Windows 8有一点不兼容,就是运行之后,任务栏会有它的图标,看着很不爽,用兼容模式运行可解决这个问题,但是这样一来就不能开机自动运行了,本文主要解决这个问题.  (参考资料 ...

  3. PHP PDO函数库详解

    PDO是一个“数据库访问抽象层”,作用是统一各种数据库的访问接口,与mysql和mysqli的函数库相比,PDO让跨数据库的使用更具有亲和力:与ADODB和MDB2相比,PDO更高效.目前而言,实现“ ...

  4. python 筛选股票

    x[0] = '0' or x[0]='6' or x[0]='3' len(x)=6 x.isdigit() *但是有的债券也是6位 *比如010007.IB

  5. 简化通过classname查找 方法

    function getClass(oParent,sclass){ var aEle=oParent.getElementsByTagName('*'); var result=[]; for(va ...

  6. 小游戏Talk表

    [Config]1|0|2|久远的记忆影子|你也是误入宠物王国的妹子吧,我在这里等你很久了,或许我们应该一起逃出这里,跟着我.[Config] [Config]2|3|2|久远的记忆影子|这里原本是一 ...

  7. win764位下安装mysql-5.6.22-x64启动服务报 系统错误 1067的解决办法

    本人电脑win7,64位,需要安装mysql服务器.版本:mysql-5.6.22-x64.安装完成后,在服务里面并没有mysql.于是在百度上搜了下,好多信息,最后把解决方法自己总结下. 在${pr ...

  8. Flapper Bird的学习笔记(二)

    因为有一个超屌的梦想,所以就绝不会做一个孬种的追梦人! 本章节主要目的: 实现小鸟的飞行. 实现管道的随机出现. 实现相机跟随. 上次主要是场景的布置,这节全力拿下代码. 1.小鸟飞行(一种制作2D动 ...

  9. 【转】Weblogic的集群

    原文链接:http://www.cnblogs.com/HondaHsu/p/4267972.html 一.Weblogic的集群 还记得我们在第五天教程中讲到的关于Tomcat的集群吗? 两个tom ...

  10. asp.net webForm登录授权

    HttpCookie cookie; cookie = FormsAuthentication.GetAuthCookie(W3Account, isRemberMe); if (isRemberMe ...