顺序表是用地址连续的存储单元顺序存储线性表中的各个数据元素, 逻辑上相邻的数据元素在物理位置上也相邻。因此,在顺序表中查找任何一个位置上的数据元素非常方便, 这是顺序存储的优点。 但是, 在对顺序表进行插入和删除时,需要通过移动数据元素来实现,影响了运行效率。线性表的另外一种存储结构——链式存储(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. c#数据绑定(5)--LINQ

    嶽永鹏/文 本实例以MS AdventureWorks2008Entities数据库为基础,演示了LINQ TO ENTITY.LINQ TO ENTITYSQL和LINQ TO ENTITYCLIE ...

  2. 如何实现自定义的android WebView错误页

    一般来说,可能第一时间想到的是自定义一个html来替代webview内置的异常页面.  但是实际操作时,这种方法比较困难. 这里介绍一个简单的替代方案,希望能有所帮助. 可以采用嵌套layout的方式 ...

  3. 对copy、mutableCopy理解

    Objective - C 中有很多在日常项目中经常用到的常用代码,在这里着重的讲一下关于copy 和 mutableCopy 的区别以及相关用法. Objective - C 中可变对象和不可对象经 ...

  4. Backbone的一点使用心得

    Backbone的其实感觉上上手很难,大概在一年前就想实践下,结果总是没有付诸行动,这次需求中狠狠心决定一定要使用一次看看,感受下. 可是第一步真的比较困难,因为直接看API好像没有感觉就在网上找实例 ...

  5. 在Spring tools suite中使用git 共享项目

    我们都在eclipse 和 myeclipse中使用过cvs 和 svn 版本控制工具进行团队开发,今天我学习了另外一种版本控制工具git,下面我演示如何在Spring tools suite中使用g ...

  6. mongodb 备份 还原 导出 导入

    张映 发表于 2013-12-03 分类目录: nosql 标签:mongodb, 备份, 导入, 导出, 还原 mongodb数据备份和还原主要分为二种,一种是针对于库的mongodump和mong ...

  7. Java 找不到主类错误

    Eclipse 运行java 程序,突然出现错误:没有或找不到主类. 在网上找了好多办法,都不行. jdk环境配置啊-->这个一般不会出错,因为以前都不会出现这种问题. 查看项目配置啥的--&g ...

  8. C# 数据类型

    1.整数类型:  int    只能 存储证书,不能存储小数. 2.小数类型: doubie 机能存储整数,也能存储小数,小数点后面的额 15-16位. 3.金钱类型: decimal:用来存储金钱, ...

  9. 点击document隐藏某个div

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. WebStorm常用配置

    设置 快捷键设置 可以采用多种风格,这里采用Visual Studio风格,便于习惯使用其它IDE的用户迁移. JavaScript版本设置 JavaScript的主流版本已升至ESMAScript6 ...