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

    页内导航是在一个页面内根据需要加载其他页面的内容,在开发基于JavaScript的Windows应用商店应用时,可以使用WinJS.Navigation.navigate函数传递要加载的页面地址并使用 ...

  2. 关于jquery中html()、text()、val()的区别

    1. .html()用为读取和修改元素的HTML标签    对应js中的innerHTML .html()是用来读取元素的HTML内容(包括其Html标签),.html()方法使用在多个元素上时,只读 ...

  3. 通过select的value值来改变textarea内字体和大小

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

  4. HDU 1513 最长子序列

    Palindrome Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total ...

  5. javascript选择排序

    function selectionSort(arr){ var index,value; for(var i = 0;i < arr.length;i ++){ index = i; //先记 ...

  6. linux环境下安装oracle数据库 原文在卡卡100http://www.cnblogs.com/kaka100

    centos55_oracle11gr2_install   第一个阶段:安装centos55 a:安装centos5.5   用图形界面安装  硬盘 16G 注意:用图形界面安装.. 第二个阶段:配 ...

  7. protocol的简单写法

    // // TouchView.h // LessonUIControlAndSubClass #import <UIKit/UIKit.h> @class TouchView; //1. ...

  8. WebStorm常用配置

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

  9. 认识WCF

    WCF 一.什么是WCF? 1.Windows Communication Foundation(WCF)是由微软发展的一组数据通信的应用程序开发接口,可以翻译为Windows通讯接口.它是.NET框 ...

  10. RabbitMQ Step by step(一) 安装

    RabbitMQ是一个消息中间件,可以存储转发消息,个人感觉优越于MSMQ RabbitMQ官方网站(http://www.rabbitmq.com)可以获取到安装文件,建议大家详细浏览官方网站,官方 ...