032204链表数据结构实现资料.docx

上传人:b****4 文档编号:3742054 上传时间:2022-11-25 格式:DOCX 页数:23 大小:1.01MB
下载 相关 举报
032204链表数据结构实现资料.docx_第1页
第1页 / 共23页
032204链表数据结构实现资料.docx_第2页
第2页 / 共23页
032204链表数据结构实现资料.docx_第3页
第3页 / 共23页
032204链表数据结构实现资料.docx_第4页
第4页 / 共23页
032204链表数据结构实现资料.docx_第5页
第5页 / 共23页
点击查看更多>>
下载资源
资源描述

032204链表数据结构实现资料.docx

《032204链表数据结构实现资料.docx》由会员分享,可在线阅读,更多相关《032204链表数据结构实现资料.docx(23页珍藏版)》请在冰豆网上搜索。

032204链表数据结构实现资料.docx

032204链表数据结构实现资料

1、课程名称:

链表数据结构实现

2、知识点

2.1、上次课程的主要知识点

包装类的使用。

2.2、本次预计讲解的知识点

1、链表的基本形式;

2、单向链表的完整实现。

3、具体内容(★★☆☆☆)

3.1、认识链表

链表=可变长的对象数组,属于动态对象数组的范畴。

对象数组有那些问题呢?

·对象数组可以保存一组对象方便开发;

·对象数组的长度固定,而且数据的删除、修改、增加处理麻烦。

所有的开发之中都100%不可能避免掉对象数组的使用。

正因为如此现在如果要想让其可以编写出便于维护的代码,那么就需要实现一个动态对象数组,那么就可以使用链表完成。

但是现在如果要想实现动态的对象数组,要考虑两个问题:

·为了适应于所有的开发要求,此对象数组要求可以保存所有的数据类型,那么一定首选Object类型;

·为了可以保存多个数据,需要采用引用的方式来进行保存,但是数据本身是不可能保存顺序的,所以需要有一个可以负责保存顺序的类来包装这个数据。

通过以上的分析就可以得出如下的结论:

·保存数据为了方便使用Object;

·数据本身不包含有先后的逻辑关系,所以将数据封装在一个Node类,负责关系的维护。

范例:

定义出如下的一个类

classNode{//表示定义的节点

privateObjectdata;//要保存的数据

privateNodenext;//保存下一个节点

publicNode(Objectdata){//有数据才可以保存节点

this.data=data;

}

publicvoidsetNext(Nodenext){//设置节点

this.next=next;

}

publicNodegetNext(){//取得节点

returnthis.next;

}

publicObjectgetData(){

returnthis.data;

}

}

完成节点之后,下面就可以进行节点的基本使用了。

范例:

采用循环的方式操作节点

publicclassTestDemo{

publicstaticvoidmain(Stringargs[]){

//1、定义各自独立的操作节点

Noderoot=newNode("火车头");

Noden1=newNode("车厢1");

Noden2=newNode("车厢2");

//2、设置彼此间的关系

root.setNext(n1);

n1.setNext(n2);

//3、输出

NodecurrentNode=root;//从根节点开始取出数据

while(currentNode!

=null){//当前有节点

System.out.println(currentNode.getData());//取出数据

currentNode=currentNode.getNext();//下一个节点

}

}

}

以上的操作如果使用循环并不方便。

最好的做法是递归调用。

范例:

利用递归的方式实现内容的取得

publicclassTestDemo{

publicstaticvoidmain(Stringargs[]){

//1、定义各自独立的操作节点

Noderoot=newNode("火车头");

Noden1=newNode("车厢1");

Noden2=newNode("车厢2");

//2、设置彼此间的关系

root.setNext(n1);

n1.setNext(n2);

print(root);

}

publicstaticvoidprint(Nodenode){

if(node==null){

return;//结束方法调用

}

System.out.println(node.getData());

print(node.getNext());

}

}

通过以上的结构讲解,应该已经清楚了链表在整个实现的关键就是Node类,Node类要保存数据与下一个节点。

3.2、链表开发入门

虽然以上的代码已经实现了链的形式,但是以上的代码之中存在有两个问题:

·用户需要自己手工定义Node类,但是实际上Node类对用户没用;

·Node类的先后关系如果交由用户处理,那么整个代码就乱了。

所以现在发现,需要有一个类,这个类可以负责所有的Node的关系匹配,而用户只需要通过这个类保存数据或取得数据即可,那么就可以编写一个Link类完成。

范例:

基本结构

classNode{//表示定义的节点

privateObjectdata;//要保存的数据

privateNodenext;//保存下一个节点

publicNode(Objectdata){//有数据才可以保存节点

this.data=data;

}

publicvoidsetNext(Nodenext){//设置节点

this.next=next;

}

publicNodegetNext(){//取得节点

returnthis.next;

}

publicObjectgetData(){

returnthis.data;

}

}

classLink{//表示一个链表操作类,利用此类来隐藏Node的节点匹配

publicvoidadd(Objectobj){//向链表里面追加数据

}

publicvoidprint(){//输出链表中的全部数据

}

}

publicclassTestDemo{

publicstaticvoidmain(Stringargs[]){

Linkall=newLink();

all.add("商品1");

all.add("商品2");

all.add("商品3");

all.print();

}

}

用户不关心Node,用户只关心通过Link操作完成后可以取得数据。

范例:

完善程序

classNode{//表示定义的节点

privateObjectdata;//要保存的数据

privateNodenext;//保存下一个节点

publicNode(Objectdata){//有数据才可以保存节点

this.data=data;

}

publicvoidsetNext(Nodenext){//设置节点

this.next=next;

}

publicNodegetNext(){//取得节点

returnthis.next;

}

publicObjectgetData(){

returnthis.data;

}

//第一次调用:

this=Link.root

//第二次调用:

this=Link.root.next

//第三次调用:

this=Link.root.next.next

publicvoidaddNode(NodenewNode){

if(this.next==null){//当前节点之后没有节点

this.next=newNode;

}else{//如果现在当前节点后有节点

this.next.addNode(newNode);

}

}

//第一次调用:

this=Link.root

//第二次调用:

this=Link.root.next

publicvoidprintNode(){

System.out.println(this.data);//当前节点数据

if(this.next!

=null){//还有后续的节点

this.next.printNode();

}

}

}

classLink{//表示一个链表操作类,利用此类来隐藏Node的节点匹配

privateNoderoot;//需要有一根元素

publicvoidadd(Objectobj){//向链表里面追加数据

//将要操作的数据包装为Node类对象,这样才可以进行先后关系的排列

NodenewNode=newNode(obj);

//现在没有根节点

if(this.root==null){//this出现在Link类,表示Link类的当前对象

this.root=newNode;//将第一个节点作为根节点

}else{//根节点存在了,交由Node类负责处理

this.root.addNode(newNode);//由根节点负责调用

}

}

publicvoidprint(){//输出链表中的全部数据

if(this.root!

=null){//现在有数据

this.root.printNode();//输出节点数据

}

}

}

publicclassTestDemo{

publicstaticvoidmain(Stringargs[]){

Linkall=newLink();

all.add("商品1");

all.add("商品2");

all.add("商品3");

all.print();

}

}

此时的代码就实现了链表的基本操作,整个过程之中,用户不关心Node的处理,只关心数据的保存和输出。

3.3、开发可用链表

以上的代码只能够说是基本的链表结构形式,但是从另外一个方面,以上的代码给我们提供了链表的实现思路,可是如何才能够设计一个好的链表呢?

链表的实现必须依靠于节点类Node类来实现,但是在整个的过程之中一定要清楚,用户不需要操作Node。

而且通过现在的代码可以发现,Node类里的操作有其特定的需要。

但是这个时候Node类写在了外面,那么就表示用户可以直接操作Node类的对象。

所以程序现在的问题在于:

如何可以让Node类只为Link类服务,但是又不让其被其他类所访问,那么自然就要想到使用内部类来完成,而且内部类的好处在于:

可以与外部类直接进行私有属性的访问。

范例:

合理的结构规划

classLinkImpl{//外部的程序只关心此类

privateclassNode{//使用私有内部类,防止外部使用此类

privateObjectdata;

privateNodenext;

publicNode(Objectdata){

this.data=data;

}

}

//***************************************************

privateNoderoot;//根元素

}

如果要开发程序,那么一定要创建出自己的操作标准,那么一旦说到标准,就应该想到使用接口来完成。

interfaceLink{

}

classLinkImplimplementsLink{//外部的程序只关心此类

privateclassNode{//使用私有内部类,防止外部使用此类

privateObjectdata;

privateNodenext;

publicNode(Objectdata){

this.data=data;

}

}

//***************************************************

privateNoderoot;//根元素

}

在随后完善代码的过程之中,除了功能的实现之外,实际上也属于接口功能的完善。

3.3.1、实现数据增加操作:

publicvoidadd(Objectdata)

1、需要在接口里面定义好数据增加的操作方法;

interfaceLink{

publicvoidadd(Objectdata);//数据增加

}

2、进行代码的实现,同样实现的过程之中LinkImpl类只关心根节点,而具体的子节点的排列都交由Node类负责处理;

·在Link类中实现add()方法:

publicvoidadd(Objectdata){

if(data==null){//现在没有要增加的数据

return;//结束调用

}

NodenewNode=newNode(data);//创建新的节点

if(this.root==null){//保留有根节点

this.root=newNode;

}else{//应该交由Node类负责处理

this.root.addNode(newNode);

}

}

·在Node类中进行数据的追加操作;

publicvoidaddNode(NodenewNode){

if(this.next==null){

this.next=newNode;

}else{

this.next.addNode(newNode);

}

}

此时的代码实现过程与基本的实现是完全一样的。

3.3.2、取得保存元素个数:

publicintsize()

每一个Link接口的对象都要保存各自的内容,所以为了方便控制保存个数,可以增加一个Link类中的属性,并且利用此属性在数据成功追加之后实现自增操作。

·在Link类中定义一个count属性,默认值为0;

privateintcount=0;

·当数据已经成功添加完毕之后实现计数的统计;

publicvoidadd(Objectdata){

if(data==null){//现在没有要增加的数据

return;//结束调用

}

NodenewNode=newNode(data);//创建新的节点

if(this.root==null){//保留有根节点

this.root=newNode;

}else{//应该交由Node类负责处理

this.root.addNode(newNode);

}

this.count++;

}

而在Link接口里面追加size()的方法,同时在LinkImpl子类里进行方法的覆写。

interfaceLink{

publicvoidadd(Objectdata);//数据增加

publicintsize();//取得保存元素的个数

}

publicintsize(){

returnthis.count;

}

此操作直接与最后的输出有关。

3.3.3、判断是否为空集合:

publicbooleanisEmpty()

如果要想判断集合是否为空,有两种方式:

长度为0,另外一个就是判断根元素是否为null。

范例:

在Link接口中追加一个新的方法:

isEmpty

interfaceLink{

publicvoidadd(Objectdata);//数据增加

publicintsize();//取得保存元素的个数

publicbooleanisEmpty();//判断是否为空集合

}

范例:

在LinkImpl类中实现此方法

publicbooleanisEmpty(){

returnthis.root==null;

}

实际上此操作与size()几乎一脉相承。

3.3.4、数据查询:

publicbooleancontains(Objectdata)

任何情况下,Link类只负责与根元素操作有关的内容,而所有的其他元素的数据的变更、查找、关系的匹配都应该交由Node类来负责处理。

1、在Link接口里面创建一个新的方法;

interfaceLink{

publicvoidadd(Objectdata);//数据增加

publicintsize();//取得保存元素的个数

publicbooleanisEmpty();//判断是否为空集合

publicbooleancontains(Objectobj);//判断是否有指定的元素

}

2、在LinkImpl子类里面要通过根元素开始调用查询,所有的查询交由Node类负责;

·在LinkImpl发出具体的查询要求之前,必须要保证有集合数据;

publicbooleancontains(Objectdata){

if(this.root==null){//没有集合数据

returnfalse;

}

returnthis.root.containsNode(data);//根元素交给Node类完成

}

·在Node类中实现数据的查询;

//第一次:

this=LinkImpl.root;

//第二次:

this=LinkImpl.root.next;

publicbooleancontainsNode(Objectdata){

if(this.data.equals(data)){//该节点数据符合于查找数据

returntrue;

}else{//继续向下查找

if(this.next!

=null){//当前节点之后还有下一个节点

returnthis.next.containsNode(data);

}else{

returnfalse;

}

}

}

这种查询的模式实质上也属于逐行的判断扫描。

3.3.5、根据索引取得数据:

publicObjectget(intindex)

既然链表属于动态的对象数组,所以数组本身一定会提供有根据索引取得数据的操作支持,那么在链表中也可以定义与之类似的方法。

但是在进行数据保存的时候并没有设置索引,那么现在有两种方案:

·修改Node类的结构,为每一个节点自动匹配一个索引,数据删除不方便;

·在操作索引时动态生成索引,适合于集合的修改。

1、修改LinkImpl类,为其增加一个foot的属性,之所以将foot属性定义在LinkImpl类之中,主要的目的是方便多个Node共同进行属性的操作使用的,同时内部类可以方便的访问外部类中的私有成员;

privateintfoot=0;//操作的索引脚标

2、在Link接口里面首先定义出新的操作方法;

publicObjectget(intindex);//根据索引取得内容,索引从0开始

3、在LinkImpl类里面定义功能实现:

·在Node类中应该提供有一个getNode()的方法,那么这个方法的功能是依次判断每一个索引值的操作形式。

publicObjectgetNode(intindex){//传递索引的序号

if(LinkImpl.this.foot++==index){//当前的索引为要查找的索引

returnthis.data;//返回当前节点对象

}else{

returnthis.next.getNode(index);

}

}

·在Link类中实现get()方法;

publicObjectget(intindex){

if(index>=this.count){//索引不存在

returnnull;

}

this.foot=0;//查询之前执行一次清零操作

returnthis.root.getNode(index);

}

这种查询的模式与contains()最大的不同一个是数字索引,一个是内容。

3.3.6、修改数据:

publicvoidset(intindex,Objectobj)

与get()相比,set()方法依然需要进行循环的判断,只不过get()索引判断成功之后会返回数据,而set只需要用新的数据更新已有节点数据即可。

1、在Link接口里面定义新的方法:

publicvoidset(intindex,Objectobj);

2、修改LinkImpl子类,流程与get()差别不大;

·在Node类里面追加一个新的setNode()方法;

publicvoidsetNode(intindex,Objectdata){

if(LinkImpl.this.foot++==index){

this.data=data;//重新保存数据

}else{

this.next.setNode(index,data);

}

}

·在LinkImpl子类里面覆写set()方法,在set()方法编写的时候也需要针对于给定的索引进行验证;

publicvoidset(intindex,Objectdata){

if(index>=this.count){//索引不存在

return;

}

this.foot=0;//查询之前执行一次清零操作

this.root.setNode(index,data);

}

set()与get()方法实际上在使用时都有一个固定的条件:

集合中的保存数据顺序应该为添加顺序。

3.3.7、数据删除:

publicvoidremove(Objectobj)

如果要进行数据的删除,那么对于整个链表而言就是节点的删除操作,而节点的删除操作过程之中需要考虑的问题:

要删除的是根节点还是子

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1