重温数据结构系列随笔:单链表(c#模拟实

重温数据结构系列随笔:单链表(c#模拟实

上一节我们讲述了数据结构的基本概念,这一节让我们来讨论下单链表的概念和实现

我从书中简单摘录下单链表概念

简单而言单链表的是通过许多节点构成,每个节点包含2个重要元素:该节点数据(数据域)和指向下个节点的地址(指针域)

这样说太枯燥了,让我们直接用c#来一步步实现

既然一个节点是由(数据域)和(指针域)构成,那我们简单DIY一个LinkNod类

///summary///单链表的节点////summarypublicclassLinkNod{//节点数据域publicobjctLinkNodData{gt;st;}//自己节点的地址publicGuidSlfAddrss{gt;st;}//下个节点的地址指针(存储位置)publicGuidNxtAddrss{gt;st;}}

继续来了解概念了,既然节点准备好了,那我们要了解节点是怎么通过指针域连接在一起的,看图

图中节点就是一个小矩形,数据域是姓名,指针域就是那个箭头所表示的指向它的后继,头节点h-zhao-Qian-....Wang

这样连接起来就是一个完整的单链表,头结点的数据域可以是任何信息,尾节点的地址域是空(他没有后继节点了)

好,代码中我们只有nod没有LinkTabl,那我们就按照上图来建立一个LinkTabl类

publicclassLinkTabl

{

//定义一个LinkNod集合

ListLinkNodlist=nwListLinkNod();

publicLinkTabl()

{

}

///summary

///进行单向链表初始化

////summary

publicvoidInitialList()

{

//添加5个节点拥有唯一的guid作为自身地址,后继节点地址为guid.mpty

for(inti=0;i5;i++)

{

list.Add

(

nwLinkNod{LinkNodData=string.Format(第{0}个节点,i+1),SlfAddrss=Guid.NwGuid(),NxtAddrss=Guid.Empty}

);

}

s

varj=0;

//将节点的指针域指向下一个节点的地址

list.ForEach

(

(linkNod)=

{

if(jlist.Count-1)

{

linkNod.NxtAddrss=list.Skip(++j).FirstOrDfault().SlfAddrss;

}

}

);

}

}

LinkTabl类包含一个LinkNod集合和一个初始方法,这个方法是先添加节点数据到集合中,然后将节点的地址域一一连接起来

肯定会有朋友问我,那么你怎么在单链表中插入数据或删除数据呢?

非常棒的问题,看图:

图中可以看出a节点的后继是b节点,a节点的指针域指向b节点,那如果在a节点和b节点中添加一个新的节点那情况又如何?

其实图中已经表达出来了,将a的指针域指向新节点,然后将新节点的指针域指向b节点

马上看代码理解

既然是添加节点那我们在LinkTabl类中添加方法就行

///summary

///添加一个新节点

////summary

///paramnam=nod节点/param

///paramnam=addIndx在indx处添加节点/param

publicvoidAddNod(LinkNodnod,intaddIndx)

{

if(this.list==null

nod==null)

{

//如果链表为空则初始链表并且添加节点

this.InitialList();

}

if(addIndx0

addIndxlist.Count)

{

thrownwIndxOutOfRangExcption(rmovIndx超出范围);

}

varlistCount=list.Count;

//注意,得到新插入节点的前一个索引位置

varprv=addIndx-1=0

listCount=0?0:addIndx-1;

//注意,得到新插入节点的后一个索引位置

varaftr=listCount=0?0:

addIndxlistCount-1?listCount-1:addIndx;

//插入后前一个节点

varprvNod=list[prv];

//插入后后一个节点

varaftrNod=list[aftr];

//将前一个节点的指针域指向新节点

nod.NxtAddrss=aftrNod.SlfAddrss;

//将新节点的指针域指向后一个节点

prvNod.NxtAddrss=nod.SlfAddrss;

//判断是否插入到最后一个位置

if(addIndx==list.Count)

{

nod.NxtAddrss=Guid.Empty;

list.Add(nod);

}

ls

{

//插入新节点

list.Insrt(addIndx,nod);

}

}

代码注释能够帮助你了解下添加节点的具体过程,请大家仔细消化下

最后是删除一个节点的情况:

和添加节点正好逆向思维,当我们删除b节点时,我们要将a节点的指针域指向c节点保证我们的单链表不被破坏

删除方法同样写在LinkTabl类中

///summary

///通过索引删除

////summary

///paramnam=rmovIndx/param

///rturns/rturns

publicvoidRmov(intrmovIndx)

{

if(this.list==null)

{

//如果链表为空则初始链表并且添加节点

this.InitialList();

}

if(rmovIndxthis.list.Count-1

rmovIndx0)

{

thrownwIndxOutOfRangExcption(rmovIndx超出范围);

}

varprIndx=rmovIndx-1;

varaftrIndx=rmovIndx+1;

varprNod=list[prIndx];

varaftrNod=list[aftrIndx];

//将被删除节点前后的指针域进行整理

prNod.NxtAddrss=aftrNod.SlfAddrss;

//删除该节点

list.Rmov(list[rmovIndx]);

}

ok,这就是单链表的一个简单理解,请大家务必牢记,因为后章的循环列表将更复杂,单链表只是一个链表的基础(以下是完整代码及输出情况)

classProgram

{

staticvoidMain(string[]args)

{

LinkTabltabl=nwLinkTabl();

//初始化

tabl.InitialList();

//尝试添加一个新节点

tabl.AddNod

(

nwLinkNod{LinkNodData=新节点,NxtAddrss=Guid.Empty,SlfAddrss=Guid.NwGuid()},

);

//删除一个节点

tabl.Rmov(1);

vari=0;

//循环显示

tabl.list.ForEach

(

(linkNod)=

{

if(i==tabl.list.Count-1)

{

Consol.Writ({0}-{1},linkNod.LinkNodData,Null);

}

ls

{

Consol.Writ({0}-,linkNod.LinkNodData);

}

i++;

}

);

Consol.RadLin();

}

}

///summary

///单链表的节点

////summary

publicclassLinkNod

{

//节点数据域

publicobjctLinkNodData{gt;st;}

//自己节点的地址

publicGuidSlfAddrss{gt;st;}

//下个节点的地址指针(存储位置)

publicGuidNxtAddrss{gt;st;}

}

///summary

///单链表

////summary

publicclassLinkTabl

{

//定义一个LinkNod集合

publicListLinkNodlist=nwListLinkNod();

publicLinkTabl()

{

}

///summary

///进行单向链表初始化

////summary

publicvoidInitialList()

{

//添加10个节点拥有唯一的guid作为自身地址,后继节点地址为guid.mpty

for(inti=0;i5;i++)

{

list.Add

(

nwLinkNod{LinkNodData=string.Format(第{0}个节点,i+1),SlfAddrss=Guid.NwGuid(),NxtAddrss=Guid.Empty}

);

}

varj=0;

//将节点的指针域指向下一个节点的地址

list.ForEach

(

(linkNod)=

{

if(jlist.Count-1)

{

linkNod.NxtAddrss=list.Skip(++j).FirstOrDfault().SlfAddrss;

}

}

);

}

///summary

///添加一个新节点

////summary

///paramnam=nod节点/param

///paramnam=addIndx在indx处添加节点/param

publicvoidAddNod(LinkNodnod,intaddIndx)

{

if(this.list==null

nod==null)

{

//如果链表为空则初始链表并且添加节点

this.InitialList();

}

if(addIndx0

addIndxlist.Count)

{

thrownwIndxOutOfRangExcption(rmovIndx超出范围);

}

varlistCount=list.Count;

//注意,得到新插入节点的前一个索引位置

varprv=addIndx-1=0

listCount=0?0:addIndx-1;

//注意,得到新插入节点的后一个索引位置

varaftr=listCount=0?0:

addIndxlistCount-1?listCount-1:addIndx;

//插入后前一个节点

varprvNod=list[prv];

//插入后后一个节点

varaftrNod=list[aftr];

//将前一个节点的指针域指向新节点

nod.NxtAddrss=aftrNod.SlfAddrss;

//将新节点的指针域指向后一个节点

prvNod.NxtAddrss=nod.SlfAddrss;

//判断是否插入到最后一个位置

if(addIndx==list.Count)

{

nod.NxtAddrss=Guid.Empty;

list.Add(nod);

}

ls

{

//插入新节点

list.Insrt(addIndx,nod);

}

}

///summary

///通过索引删除

////summary

///paramnam=rmovIndx/param

///rturns/rturns

publicvoidRmov(intrmovIndx)

{

if(this.list==null)

{

//如果链表为空则初始链表并且添加节点

this.InitialList();

}

if(rmovIndxthis.list.Count-1

rmovIndx0)

{

thrownwIndxOutOfRangExcption(rmovIndx超出范围);

}

varprIndx=rmovIndx-1;

varaftrIndx=rmovIndx+1;

varprNod=list[prIndx];

varaftrNod=list[aftrIndx];

//将被删除节点前后的指针域进行整理

prNod.NxtAddrss=aftrNod.SlfAddrss;

//删除该节点

list.Rmov(list[rmovIndx]);

}

}

输出:

希望大家对单链表有比较深的理解,其实在效率性能上这样的单链表不及数组,因为数组更本没有那么繁琐,

大家在实际项目还是用数组比较好,下章会和大家先补充下c#中的LinkList类和Array类的区别(*数组和链表的区别(很重要)),

然后简单说下循环链表。









































北京最权威治疗白癜风医院
怎样防治白癜风



转载请注明:http://www.92nongye.com/hxjs/204612532.html