数据结构实例精讲链表文档格式.docx
《数据结构实例精讲链表文档格式.docx》由会员分享,可在线阅读,更多相关《数据结构实例精讲链表文档格式.docx(77页珍藏版)》请在冰豆网上搜索。
如果输入的数据为指定的结束标志,转到⑦;
⑤若是空表,将新结点链接到表头;
若是非空表,将新结点链接到表尾;
⑥转到③;
⑦返回头指针。
采用倒挂法的步骤与正挂法差不多,只是插入新结点时,总是将其插入到表头。
(2)源程序。
#include<
iostream>
usingnamespacestd;
typedefintElemType;
structLNode
{ElemTypedata;
LNode*next;
};
typedefLNode*LinkList;
LinkListcreate();
LinkListcreate1();
voidprint(LinkListL);
voidDestroyList(LinkList&
L);
intmain()
{
LinkListL;
cout<
<
"
输入链表中的数据,以-1作为结束:
endl;
L=create();
cout<
采用正挂法建立的单链表为:
print(L);
DestroyList(L);
L=create1();
采用倒挂法建立的单链表为:
return0;
LinkListcreate()//采用正挂的方法建立不带头结点的单链表
LinkListp1,p2,head;
intnum;
head=NULL;
//创建一个空表
cin>
>
num;
while(num!
=-1)//如果输入的数据为指定的结束标志,结束
{
p1=newLNode;
//利用new运算符向系统申请分配一个结点
p1->
data=num;
next=NULL;
if(head==NULL)
head=p1;
//若是空表,将新结点链接到表头
else
p2->
next=p1;
//若是非空表,将新结点链接到表尾
p2=p1;
}
returnhead;
//返回头指针
LinkListcreate1()//采用倒挂的方法建立不带头结点的单链表
LinkListp,head;
=-1)
p=newLNode;
p->
next=head;
head=p;
//插入到表头
voidprint(LinkListL)
LinkListp;
p=L;
if(p==NULL)
链表是空表!
else
while(p->
next!
=NULL)
{
p->
data<
->
"
;
p=p->
next;
}
data<
endl;
L)
LinkListp=L;
while(p!
L=L->
deletep;
p=L;
(3)应用于后面实例的头文件LinkList.h和LinkList.cpp文件。
本例实现了链表的创建和输出等基本运算,为了在后面的实例中直接应用这些基本运算,将链表的定义及函数声明保存到LinkList.h头文件中,各函数的实现保存到LinkList.cpp中。
头文件LinkList.h保存内容如下:
源程序文件LinkList.cpp保存内容如下:
#include"
Linklist.h"
p1->
data;
//输入结点的数据域
while(p1->
data!
head=p1;
deletep1;
这样,在下面的实例中如果需要用到这些基本运算,只需要#include"
LinkList.h"
包含头文件即可。
2.4链表的操作实例
1.插入和删除
链表的一个重要特点是插入、删除操作灵活方便,不需移动结点,只需改变结点中指针域的值即可。
【实例2-13】链表有序插入。
编写一个函数voidInsert(LinkList&
L,ElemTypenum)在链表L中插入一个值为num的结点,插入后链表仍保持有序。
设单链表L中的结点按数据域data的值从小到大的顺序排列。
并调用所编写的有序插入函数建立一个有序链表。
在插入时,有三种情况:
①插入到一个空表中;
②插入到链表的首部;
③插入到链表中间或链表的最后。
其中,前两种情况会改变头指针。
而后一种情况,需要先在单链表中寻找到正确的插入位置(设寻找到的插入位置在第i-1个结点之后)并由指针p指示,然后通过修改指针,重新建立ai-1与num、num与ai两两数据元素之间的链,从而实现三个元素之间链接关系的变化。
num插入时指针变化如图2-2所示,图中虚线所示为插入前的指针。
图2-2单链表的插入
(2)函数程序代码。
voidInsert(LinkList&
L,ElemTypenum)
LinkListp,s;
s=newLNode;
s->
data=num;
if(p==NULL)//插入到空表
{
L=s;
s->
elseif(p->
data>
num)//插入到链表首
next=L;
else//插入到中间或最后
while(p->
next&
&
next->
num)
next=p->
next=s;
(3)函数应用的示例源程序。
L,ElemTypenum);
ElemTypenum;
L=NULL;
Insert(L,num);
cin>
采用插入法建立的有序单链表为:
【实例2-14】删除最小值。
在单链表中删除最小值结点。
若要在L为头指针的单链表中,删除数据域值最小的结点。
首先要搜索单链表以找到指定删除结点(以指针q指示)的前驱结点(以指针pre指示),然后仅需修改结点pre中的指针域,最后释放待删除结点q所占的存储空间。
图2-3显示了删除时指针变化情况,虚线所示为删除前的指针。
图2-3单链表的结点删除
一般来说,在链表中指定数据域的结点时,有四种情况要考虑:
①链表是一个空表;
②要删除的结点在链表的首部(表头指针被修改);
③要删除的结点在链表中间或最后;
④要删除的结点在链表中不存在。
单链表中删除最小值结点,为使结点删除后不出现“断链”,应知道被删结点的前驱。
而“最小值结点”是在遍历整个链表后才能知道。
所以算法应首先遍历链表,求得最小值结点及其前驱。
遍历结束后再执行删除操作。
将“在单链表中删除最小值结点”这一操作写成一个函数DeleteMin。
函数体中,用指针p来遍历单链表,用指针q指向最小值结点,指针pre指向最小值结点q的前驱结点。
voidDeleteMin(LinkList&
//L是不带头结点的单链表,算法删除其最小值结点。
LinkListp,pre,q;
if(L==NULL)
return;
//链表为空,无结点可删,直接返回
//p为工作指针,指向待处理的结点。
此时链表非空。
pre=NULL;
//pre用于指向最小值结点的前驱
q=p;
//q指向最小值结点,初始假定第1个结点是最小值结点
next!
if(p->
q->
data)//查最小值结点
{
pre=p;
q=p->
}
//p指针后移。
if(pre==NULL)
L=L->
next;
//头结点是最小值结点
pre->
next=q->
//最小值结点不是头结点
deleteq;
//释放最小值结点空间
链表初始情况为:
DeleteMin(L);
删除最小值后,链表为:
}voidDeleteMin(LinkList&
通常可以在线性链表的第一个结点之前附设一个结点,称之为头结点。
头结点的数据域可以不存储任何信息,也可存储如线性表的长度等附加信息,头结点的指针域存储指向第一个结点的指针,而链表的头指针指向头结点。
这样,在第一个结点前面插入新结点和删除第一个结点就不影响表头指针L的值(如上面算法就引起了头指针L的变化),而只改变头结点的指针域的值,因此就可以和其他位置的插入、删除同样处理了。
例如,若L为带头结点的单链表,则插入函数Insert可简单地写成:
{//L是带头结点的递增有序单链表,插入num后仍保持有序
在双向链表中,若p是指向表中某个结点的指针,则有
p==p->
prior==p->
prior->
next
双向链表的插入和删除算法与单链表有很大的不同,需同时修改两个方向上的指针。
例如,在双向链表中指针p所指的结点之前插入元素x的操作步骤为:
s=newDuLNode;
s->
data=x;
prior=p->
prior;
next=s;
next=p;
prior=s;
删除双向链表中指针p所指的结点的操作步骤为:
next=p->
deletep;
【实例2-15】删除重复结点。
编写一个函数,删除不带头结点的单链表L中的重复结点。
用指针p来遍历单链表,p的初始值为单链表的头指针。
p指向一个数据结点时,从它的后继结点开始到表的结束,找与其值相同的结点并删除之;
然后p指向下一个结点;
依此类推,直到p指向最后结点时,遍历结束。
voidDeleteSame(LinkList&
//L是不带头结点的单链表,算法删除其重复结点。
LinkListp,q,r;
//p指向第一个结点
if(p==NULL)
return;
next)
while(q->
next)//从p的后继开始找重复结点
if(q->
data==p->
data)
r=q->
//找到重复结点,用r指向该结点,删除r
q->
next=r->
deleter;
q=q->
if(p->
=NULL)
p=p->
}
DeleteSame(L);
删除重复结点后,链表为:
【实例2-16】集合的差集。
已知两个带头结点的递增有序的单链表La、Lb分别存储集合A和B,编写一个函数求两个集合A和B的差集A=A-B(即仅由在A中出现而不在B中出现的元素所构成的集合)。
求两个集合A和B的差集A=A-B,即在A中删除A和B中共有的元素。
由于集合用单链表存储,问题变成删除链表中的结点问题。
删除结点时,要记住被删除结点的前驱,以便顺利删除被删结点。
由于两个链表递增有序,因此遍历时,两链表均从第1个结点开始,直到其中一个链表到尾为止。
voidDifference(LinkList&
La,LinkListLb)
//La和Lb是带头结点的递增有序的单链表,算法求两集合的差集A=A-B
LinkListpa,pb,pre;
pa=La->
//pa和pb分别是链表La和Lb的工作指针
pb=Lb->
pre=La;
//pre为La中pa所指结点的前驱结点的指针
while(pa!
=NULL&
pb!
if(pa->
pb->
pre=pa;
pa=pa->
//La表中当前结点指针后移
elseif(pa->
data>
pb=pb->
//Lb表中当前结点指针后移
pre->
next=pa->
//处理A、B中元素值相同的结点,应删除
deletepa;
pa=pre->
La,LinkListLb);
LinkListLa,Lb;
输入集合A中的数据,以-1作为结束:
La=newLNode;
La->
next=NULL;
Insert(La,num);
集合A初始情况为:
print(La->
next);