单向链表操作.docx
- 文档编号:30694801
- 上传时间:2023-08-19
- 格式:DOCX
- 页数:15
- 大小:21.01KB
单向链表操作.docx
《单向链表操作.docx》由会员分享,可在线阅读,更多相关《单向链表操作.docx(15页珍藏版)》请在冰豆网上搜索。
单向链表操作
单向链表
下例定义了一个单向链表的模板类,它分别实现增加、删除、寻找和打印操作。
增加时,Add()成员函数在链首挂接上一个携有T类型对象的结点,使之成为链首结点,
由下列步骤①、②、③、④完成:
①从堆空间中申请一个结点;
②将T类对象挂接在这个结点上;
③将该结点指向链首的结点;
④将该结点成为链首(链首指针pFirst指向它)。
删除时,Remove()成员函数寻找到挂接该对象的结点,先脱链,再删除结点。
由下列步骤⑤、⑥、⑦完成:
⑤链中待删结点前后的两个结点链接起来,以使待删结点脱链;
⑥删除待删结点上的T类对象(将空间还给堆);
⑦删除待删结点(将空间还给堆)。
如果找不到对应结点,则无功而返;如果找到的是链首结点,则步骤⑤的脱链,由链首指针pFirst指向下一个结点来完成;删除结点须先将挂接的对象删除。
寻找时,Find()成员函数从链首开始遍历整个链表,查找是否有结点含有对应对象,若无,则返回空指针。
打印时,PrintList()成员函数从链首开始遍历整个链表,打印每个结点下的对象值。
链表类析构的任务是把链表中所有的结点空间收回,包括挂接在结点下的T类对象。
它从链首开始遍历整个链表,处理结点时,先删除挂接的对象,再删除结点,然后去处理下一个结点。
例9.3.2:
#include
#include
template
classList{ //定义通用链表类模板
public:
List(); //声明构造函数
voidAdd(T&); //声明增加节点的成员函数
voidRemove(T&); //声明删除节点的成员函数
T*Find(T&); //声明查找节点的成员函数
voidPrintList(); //声明打印链表的成员函数
~List(); //声明析构函数
protected:
structNode{ //定义结点的结构类型
Node*pNext; //指向下一个结点的指针成员
T*pT; //指向本结点数据的指针成员
};
Node*pFirst; //指向链表头节点的指针
};
template
:
List() //定义链表的构造函数
{
pFirst=NULL; //链表首指针设为空指针
}
template
voidList
:
Add(T&t) //定义增加节点的成员函数
{
Node*temp=newNode; //向堆内存申请一个结点的内存空间
temp->pT=&t; //将新节点的pT指向新结点的数据
temp->pNext=pFirst; //将链表头指针赋给新节点的pNext
pFirst=temp; //将新节点的地址赋给头指针
}
template
voidList
:
Remove(T&t) //定义删除节点的成员函数
{
Node*q=0;
if(*(pFirst->pT)==t)//判断要删除的节点是否是头节点,如是,链首指针指向下一个节点,把第一个节点脱链
{
q=pFirst;
pFirst=pFirst->pNext;
}
else //顺链查找
{
for(Node*p=pFirst;p->pNext;p=p->pNext)
if(*(p->pNext->pT)==t)//链中待删结点前后的两个结点链接起来,以使待删结点脱链
{
q=p->pNext;
p->pNext=q->pNext;
break;
}
}
if(q){
deleteq->pT; //删除待删结点上的T类的对象
deleteq; //删除待删结点
}
}
template
T*List
:
Find(T&t) //定义寻找节点的成员函数
{
for(Node*p=pFirst;p;p=p->pNext) //当节点指针不为空指针时循环
{
if(*(p->pT)==t) //如找到了要找的节点返回指向该节点的指针
returnp->pT;
}
return0; //如找不到返回空指针
}
template
voidList
:
PrintList() //定义打印链表的成员函数
{
for(Node*p=pFirst;p;p=p->pNext) //当节点指针不为空指针时循环
{
cout<<*(p->pT)<<"";//显示p指针所指向结点的数据值
}
cout< } template List : ~List() //定义链表类的析构函数 { Node*p; while(pFirst! =NULL)//删除链表中所有结点,释放结点数据所空间 {p=pFirst; pFirst=pFirst->pNext; deletep->pT; deletep; } } voidmain() { List List for(inti=1;i<7;i++) { floatList.Add(*newfloat(i+0.6));//调用成员函数Add intList.Add(*newint(i));//调用成员函数Add } floatList.PrintList();//调用成员函数PrintList() intList.PrintList();//调用成员函数PrintList() floatb=3.6; float*pf=floatList.Find(b);//调用成员函数Find() if(pf) floatList.Remove(*pf);//调用成员函数Remove() floatList.PrintList();//调用成员函数PrintList() intc=3; int*pi=intList.Find(c);//调用成员函数Find() if(pi) intList.Remove(*pi);//调用成员函数Remove() intList.PrintList();//调用成员函数PrintList() getch(); } 程序运行结果如下: 6.65.64.63.62.61.6 654321 6.65.64.62.61.6 65421 说明: 在主函数中,分别用float和int类型对类模板List实例化,生成二种具体的类,由这二种具体的类创建了2个链表对象floatList和intList,然后这2个对象分别调用各自的增加节点的成员函数Add(),程序先从堆中申请分配float或int类型的空间,初始化,然后作为参数传递给Add(),Add()马上从堆中申请分配结点空间,挂接该float或int变量,然后作为链首链入链表中。 程序然后调用PrintList()函数打印链表,调用查找Find()函数查找某一节点,找到了删除掉,最后打印出经删除操作后的链表各节点数据。 3、类模板与模板类的区别 类模板是模板的定义,不是一个实实在在的类,定义中用到通用类型参数。 模板类是实实在在的类定义,是类模板的实例化。 类定义中参数被实际类型所代替。 单向链表操作详解 structstudent*p; /*临时指针变量*/ structstudent*q; /*临时指针变量*/ first=head->next; /*原链表剩下用于直接插入排序的节点链表: 可根据图12来理解。 */ head->next=null; /*只含有一个节点的链表的有序链表: 可根据图11来理解。 */ while(first! =null) /*遍历剩下无序的链表*/ { /*注意: 这里for语句就是体现直接插入排序思想的地方*/ for(t=first,q=head;((q! =null)&&(q->num /*退出for循环,就是找到了插入的位置*/ /*注意: 按道理来说,这句话可以放到下面注释了的那个位置也应该对的,但是就是不能。 原因: 你若理解了上面的第3条,就知道了。 */ first=first->next; /*无序链表中的节点离开,以便它插入到有序链表中。 */ if(q==head) /*插在第一个节点之前*/ { head=t; } else /*p是q的前驱*/ { p->next=t; } t->next=q; /*完成插入动作*/ /*first=first->next;*/ } returnhead; } /* ========================== 功能: 冒泡排序(由小到大) 返回: 指向链表表头的指针 ========================== */ /* 直接插入排序的基本思想就是对当前还未排好序的范围内的全部节点, 自上而下对相邻的两个节点依次进行比较和调整,让键值(就是用它排 序的字段,我们取学号num为键值)较大的节点往下沉,键值较小的往 上冒。 即: 每当两相邻的节点比较后发现它们的排序与排序要求相反时, 就将它们互换。 单向链表的冒泡排序图示: ---->[1]---->[3]---->[2]...---->[n]---->[null](原链表) head 1->next 3->next 2->next n->next ---->[1]---->[2]---->[3]...---->[n]---->[null](排序后链表) head 1->next 2->next 3->next n->next 图14: 有n个节点的链表冒泡排序 任意两个相邻节点p、q位置互换图示: 假设p1->next指向p,那么显然p1->next->next就指向q, p1->next->next->next就指向q的后继节点,我们用p2保存 p1->next->next指针。 即: p2=p1->next->next,则有: [ ]---->[p]---------->[q]---->[ ](排序前) p1->next p1->next->next p2->next 图15 [ ]---->[q]---------->[p]---->[ ](排序后) 图16 1、排序后q节点指向p节点,在调整指向之前,我们要保存原p的指向节点地址,即: p2=p1->next->next; 2、顺着这一步一步往下推,排序后图16中p1->next->next要指的是p2->next,所以p1->next->next=p2->next; 3、在图15中p2->next原是q发出来的指向,排序后图16中q的指向要变为指向p的,而原来p1->next是指向p的,所以p2->next=p1->next; 4、在图15中p1->next原是指向p的,排序后图16中p1->next要指向q,原来p1->next->next(即p2)是指向q的,所以p1->next=p2; 5、至此,我们完成了相邻两节点的顺序交换。 6、下面的程序描述改进了一点就是记录了每次最后一次节点下沉的位置,这样我们不必每次都从头到尾的扫描,只需要扫描到记录点为止。 因为后面的都已经是排好序的了。 */ structstudent*bubblesort(structstudent*head) { structstudent*endpt; /*控制循环比较*/ structstudent*p; /*临时指针变量*/ structstudent*p1; structstudent*p2; p1=(structstudent*)malloc(len); p1->next=head; /*注意理解: 我们增加一个节点,放在第一个节点的前面,主要是为了便于比较。 因为第一个节点没有前驱,我们不能交换地址。 */ head=p1; /*让head指向p1节点,排序完成后,我们再把p1节点释放掉*/ for(endpt=null;endpt! =head;endpt=p) /*结合第6点理解*/ { for(p=p1=head;p1->next->next! =endpt;p1=p1->next) { if(p1->next->num>p1->next->next->num) /*如果前面的节点键值比后面节点的键值大,则交换*/ { p2=p1->next->next; /*结合第1点理解*/ p1->next->next=p2->next; /*结合第2点理解*/ p2->next=p1->next; /*结合第3点理解*/ p1->next=p2; /*结合第4点理解*/ p=p1->next->next; /*结合第6点理解*/ } } } p1=head; /*把p1的信息去掉*/ head=head->next; /*让head指向排序后的第一个节点*/ free(p1); /*释放p1*/ p1=null; /*p1置为null,保证不产生“野指针”,即地址不确定的指针变量*/ returnhead; } /* ========================== 功能: 插入有序链表的某个节点的后面(从小到大) 返回: 指向链表表头的指针 ========================== */ /* 有序链表插入节点示意图: ---->[null](空有序链表) head 图18: 空有序链表(空有序链表好解决,直接让head指向它就是了。 ) 以下讨论不为空的有序链表。 ---->[1]---->[2]---->[3]...---->[n]---->[null](有序链表) head 1->next 2->next 3->next n->next 图18: 有n个节点的有序链表 插入node节点的位置有两种情况: 一是第一个节点前,二是其它节点前或后。 ---->[node]---->[1]---->[2]---->[3]...---->[n]---->[null] head node->next 1->next 2->next 3->next n->next 图19: node节点插在第一个节点前 ---->[1]---->[2]---->[3]...---->[node]...---->[n]---->[null] head 1->next 2->next 3->next node->next n->next 图20: node节点插在其它节点后 structstudent*p; /*临时指针变量*/ structstudent*q; /*临时指针变量*/ first=head->next; /*原链表剩下用于直接插入排序的节点链表: 可根据图12来理解。 */ head->next=null; /*只含有一个节点的链表的有序链表: 可根据图11来理解。 */ while(first! =null) /*遍历剩下无序的链表*/ { /*注意: 这里for语句就是体现直接插入排序思想的地方*/ for(t=first,q=head;((q! =null)&&(q->num /*退出for循环,就是找到了插入的位置*/ /*注意: 按道理来说,这句话可以放到下面注释了的那个位置也应该对的,但是就是不能。 原因: 你若理解了上面的第3条,就知道了。 */ first=first->next; /*无序链表中的节点离开,以便它插入到有序链表中。 */ if(q==head) /*插在第一个节点之前*/ { head=t; } else /*p是q的前驱*/ { p->next=t; } t->next=q; /*完成插入动作*/ /*first=first->next;*/ } returnhead; } /* ========================== 功能: 冒泡排序(由小到大) 返回: 指向链表表头的指针 ========================== */ /* 直接插入排序的基本思想就是对当前还未排好序的范围内的全部节点, 自上而下对相邻的两个节点依次进行比较和调整,让键值(就是用它排 序的字段,我们取学号num为键值)较大的节点往下沉,键值较小的往 上冒。 即: 每当两相邻的节点比较后发现它们的排序与排序要求相反时, 就将它们互换。 单向链表的冒泡排序图示: ---->[1]---->[3]---->[2]...---->[n]---->[null](原链表) head 1->next 3->next 2->next n->next ---->[1]---->[2]---->[3]...---->[n]---->[null](排序后链表) head 1->next 2->next 3->next n->next 图14: 有n个节点的链表冒泡排序 任意两个相邻节点p、q位置互换图示: 假设p1->next指向p,那么显然p1->next->next就指向q, p1->next->next->next就指向q的后继节点,我们用p2保存 p1->next->next指针。 即: p2=p1->next->next,则有: [ ]---->[p]---------->[q]---->[ ](排序前) p1->next p1->next->next p2->next 图15 [ ]---->[q]---------->[p]---->[ ](排序后) 图16 1、排序后q节点指向p节点,在调整指向之前,我们要保存原p的指向节点地址,即: p2=p1->next->next; 2、顺着这一步一步往下推,排序后图16中p1->next->next要指的是p2->next,所以p1->next->next=p2->next; 3、在图15中p2->next原是q发出来的指向,排序后图16中q的指向要变为指向p的,而原来p1->next是指向p的,所以p2->next=p1->next; 4、在图15中p1->next原是指向p的,排序后图16中p1->next要指向q,原来p1->next->next(即p2)是指向q的,所以p1->next=p2; 5、至此,我们完成了相邻两节点的顺序交换。 6、下面的程序描述改进了一点就是记录了每次最后一次节点下沉的位置,这样我们不必每次都从头到尾的扫描,只需要扫描到记录点为止。 因为后面的都已经是排好序的了。 */ structstudent*bubblesort(structstudent*head) { structstudent*endpt; /*控制循环比较*/ structstudent*p; /*临时指针变量*/ structstudent*p1; structstudent*p2; p1=(structstudent*)malloc(len); p1->next=head; /*注意理解: 我们增加一个节点,放在第一个节点的前面,主要是为了便于比较。 因为第一个节点没有前驱,我们不能交换地址。 */ head=p1; /*让head指向p1节点,排序完成后,我们再把p1节点释放掉*/ for(endpt=null;endpt! =head;endpt=p) /*结合第6点理解*/ { for(p=p1=head;p1->next->next! =endpt;p1=p1->next) { if(p1->next->num>p1->next->next->num) /*如果前面的节点键值比后面节点的键值大,则交换*/ { p2=p1->next->next; /*结合第1点理解*/ p1->next->next=p2->next; /*结合第2点理解*/ p2->next=p1->next; /*结合第3点理解*/ p1->next=p2; /*结合第4点理解*/ p=p1->next->next; /*结合第6点理解*/ } } } p1=head; /*把p1的信息去掉*/ head=head->next; /*让head指向排序后的第一个节点*/ free(p1); /*释放p1*/ p1=null; /*p1置为null,保证不产生“野指针”,即地址不确定的指针变量*/ returnhead; } /* ========================== 功能: 插入有序链表的某个节点的后面(从小到大) 返回: 指向链表表头的指针 ========================== */ /* 有序链表插入节点示
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 单向 操作