C#数据结构与算法揭秘三 链表

上文我们讨论了一种最简单的线性结构——顺序表,这节我们要讨论另一种线性结构——链表。

什么是链表了,不要求逻辑上相邻的数据元素在物理存储位置上也相邻存储的线性结构称之为链表。举个现实中的例子吧,假如一个公司召开了视频会议的吧,能在北京总公司人看到上海分公司的人,他们就好比是逻辑上相邻的数据元素,而物理上不相连。这样就好比是个链表。 链表分为①单链表,②单向循环链表,③双向链表,④双向循环链表。

介绍各种各样链表之前,我们要明白这样一个概念。什么是结点。在存储数据元素时,除了存储数据元素本身的信息外,还要存储与它相邻的数据元素的存储地址信息。这两部分信息组成该数据元素的存储映像(Image),称为结点(Node)。在c语言这些面向过程语言中,实现节点是通过指针的形式,在。net中,是通过模拟指针——类对象嵌套的形式。

然后,首先,介绍单链表。如果结点的引用域只存储该结点直接后继结点的存储地址, 则该链表叫单链表(Singly Linked List)。把该引用域叫 next。单链表结点的结构如图所示,图中 data表示结点的数据域。

现实中,就像一队盲人过马路。如图所示

把单链表结点看作是一个类,类名为 Node<T>。单链表结点类的实现如下所示。 


代码如下:

public class Node<T>
{
//一队盲人过马路 存储的是盲人的姓名
private T data; //数据域 存储数据的对象
//一队盲人过马路 指向的是下一个盲人对象。
private Node<T> next; //引用域 指向下一个对象

//构造器
public Node(T val, Node<T> p)
{
data = val;
next = p;
}

//构造器
public Node(Node<T> p)
{
next = p;
}

//构造器
public Node(T val)
{
data = val;
next = null;
}

//构造器
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;
}
}
}

下图是线性表(a1,a2,a3,a4,a5,a6)对应的链式存储结构示意图。

单链表类 LinkList<T>源代码的实现说明如下所示。首先申明一下,他继承与IListDS这个接口。

//这是一个盲人过马路的类的模拟

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()
{
Node<T> p = head;

int len = 0;
while (p != null)
{
++len;
p = p.Next;
}
return len;
}

//不让盲人排队,就是让这个队的头都不存在

//清空单链表
public void Clear()
{
head = null;
}

//判断一个盲人队列的是不是为空,看他的头部是不是有人

//判断单链表是否为空
public bool IsEmpty()
{
if (head == null) 
{
return true;
}
else
{
return false;
}
}

//在单链表的末尾添加新元素
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;

不懂的一切尽在图例中

这个方法的算法复杂度是O(n)
}

//就是在一队中增加了插队的人员

//在单链表的第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 = 1;

while (p.Next != null&& j < i)
{
r = p;
p = p.Next;
++j;
}

if (j == i)
{
Node<T> q = new Node<T>(item);
q.Next = p;
r.Next = q;
}

一切尽在图例中

}

这个方法的算法复杂度O(n)

//删除单链表的第i个结点
public T Delete(int i)
{

//是不是盲人排队的 或者排队的位置不是正确的   这就返回了一个错误信息
if (IsEmpty()|| i < 0)
{
Console.WriteLine("Link is empty or Position is
error!");
return default(T);
}
//是头结点的 的就返回  第二个节点顶到第一个节点的位置

Node<T> q = new Node<T>();

if (i == 1)
{
q = head;
head = head.Next;
return q.Data;
}
此步骤为O(1)

//不是的头位置的吧,就寻找相应位置的节点,在进行删除。他这个排队前面的人指向后面的人。这就是新的队伍了  没找到,就返回错误。
Node<T> p = head;
int j = 1;

while (p.Next != null&& j < i)
{
++j;
q = p;
p = p.Next;
}

if (j == i)
{
q.Next = p.Next;
return p.Data;
}
else
{
Console.WriteLine("The ith node is not exist!");
return default(T);
}

不懂的如图所示:

此方法的运行时间复杂度是O(n)
}

//获得单链表的第i个数据元素

//知道队伍 我要查询出队伍中第n个人是那位,
public T GetElem(int i)
{

//如果是空的就返回为错误的结果
if (IsEmpty())
{
Console.WriteLine("List is empty!");
return default(T);
}

//从图接点数  第n个的结果了
Node<T> p = new Node<T>();
p = head;
int j = 1;

while (p.Next != null&& j < i)
{

++j;
p = p.Next;
}
//有着 则返回  没有就返回错误

if (j == i)
{
return p.Data;
}
else
{
Console.WriteLine("The ith node is not exist!");
return default(T);
}
}

不懂的,一切尽在图例中。

此方法的时间的复杂度是O(n)

//我要查询张山的位于队伍的第几个位置

//在单链表中查找值为value的结点
public int Locate(T value)
{

//空返回为假的的  
if(IsEmpty())
{
Console.WriteLine("List is Empty!");
return -1;
}

Node<T> p = new Node<T>();
p = head;
int i = 1;

//从头遍历 比较器 相等的   返回为相应的索引

while (!p.Data.Equals(value)&& p.Next != null)
{
P = p.Next;
++i; 
}
不懂的,如图所示:

return i;

这个算法复杂度是O(n2)
}

}

这节我们讨论链表的基本操作,并且画图以证明,下届中我们将讨论双向链表,环形链表 应用举例。

(0)

相关推荐

  • C#数据结构之循环链表的实例代码

    复制代码 代码如下: public class Node    {        public object Element;        public Node Link; public Node()        {            Element = null;            Link = null;        } public Node(object theElement)        {            Element = theElement;      

  • C#实现单链表(线性表)完整实例

    本文实例讲述了C#实现单链表(线性表)的方法.分享给大家供大家参考,具体如下: 顺序表由连续内存构成,链表则不同.顺序表的优势在于查找,链表的优势在于插入元素等操作.顺序表的例子:http://www.jb51.net/article/87605.htm 要注意的是,单链表的Add()方法最好不要频繁调用,尤其是链表长度较长的时候,因为每次Add,都会从头节点到尾节点进行遍历,这个缺点的优化方法是将节点添加到头部,但顺序是颠倒的. 所以,在下面的例子中,执行Purge(清洗重复元素)的时候,没有

  • c#泛型学习详解 创建线性链表

    术语表 generics:泛型type-safe:类型安全collection: 集合compiler:编译器run time:程序运行时object: 对象.NET library:.Net类库value type: 值类型box: 装箱unbox: 拆箱implicity: 隐式explicity: 显式linked list: 线性链表node: 结点indexer: 索引器 泛型是什么? 很多人觉得泛型很难理解.我相信这是因为他们通常在了解泛型是用来解决什么问题之前,就被灌输了大量的理论

  • C#双向链表LinkedList排序实现方法

    本文实例讲述了C#双向链表LinkedList排序实现方法.分享给大家供大家参考.具体如下: 1.函数 打印链表函数PrintLinkedList 和 排序函数SortLinkedList 注:下面代码中的链表每项都是double类型,如果换做其他的类型或结构,则需要适当修改 /// <summary> /// 打印链表各结点信息 /// </summary> /// <param name="ll"></param> private s

  • C#实现的简单链表类实例

    本文实例讲述了C#实现的简单链表类.分享给大家供大家参考.具体如下: 一.关于C#链表 C#链表可用类LinkedList来存放.本文中的类MyLinkedList只是实现了该类的最基本的功能.C#中没有指针,但因为C#中类在赋值时传递的是地址,因此仍然可以利用这点制作一个链表. 二.结点类Node和链表类MyLinkedList代码 /// <summary> /// 链表结点 /// </summary> class Node { //结点数据,前后结点 public obje

  • C#如何自定义线性节点链表集合

    本例子实现了如何自定义线性节点集合,具体代码如下: using System; using System.Collections; using System.Collections.Generic; namespace LineNodeDemo { class Program { static void Main(string[] args) { LineNodeCollection lineNodes = new LineNodeCollection(); lineNodes.Add(new

  • C#数据结构与算法揭秘四 双向链表

    首先,明白什么是双向链表.所谓双向链表是如果希望找直接前驱结点和直接后继结点的时间复杂度都是 O(1),那么,需要在结点中设两个引用域,一个保存直接前驱结点的地址,叫 prev,一个直接后继结点的地址,叫 next,这样的链表就是双向链表(Doubly Linked List).双向链表的结点结构示意图如图所示. 双向链表结点的定义与单链表的结点的定义很相似, ,只是双向链表多了一个字段 prev.其实,双向链表更像是一根链条一样,你连我,我连你,不清楚,请看图. 双向链表结点类的实现如下所示

  • C#定义并实现单链表实例解析

    本文以实例详细描述了C#定义并实现单链表的过程及原理.一般来说C#定义并实现单链表,代码包括构成链表的结点定义.用变量来实现表头.清空整个链表 .链表复位,使第一个结点成为当前结点.判断链表是否为空.判断当前结点是否为最后一个结点.返回当前结点的下一个结点的值,并使其成为当前结点.将当前结点移出链表,下一个结点成为当前结点等内容. 具体实现代码如下所示: using System; using System.IO; // 构成链表的结点定义 public class Node { public

  • c# 自定义泛型链表类的详解

    (1)自定义泛型链表类. 复制代码 代码如下: public class GenericList<T>    {        private class Node        {            //当前节点值            private T data;            public T Data            {                get { return data; }                set { data = value; } 

  • C#数据结构之单链表(LinkList)实例详解

    本文实例讲述了C#数据结构之单链表(LinkList)实现方法.分享给大家供大家参考,具体如下: 这里我们来看下"单链表(LinkList)".在上一篇<C#数据结构之顺序表(SeqList)实例详解>的最后,我们指出了:顺序表要求开辟一组连续的内存空间,而且插入/删除元素时,为了保证元素的顺序性,必须对后面的元素进行移动.如果你的应用中需要频繁对元素进行插入/删除,那么开销会很大. 而链表结构正好相反,先来看下结构: 每个元素至少具有二个属性:data和next.data

随机推荐