数据结构知识点汇总.docx
《数据结构知识点汇总.docx》由会员分享,可在线阅读,更多相关《数据结构知识点汇总.docx(35页珍藏版)》请在冰豆网上搜索。
数据结构知识点汇总
第二章
2.1线性表的概念及其抽象数据类型的定义
2.1.1线性表的逻辑结构
线性表的定义
线性表是n个类型相同的数据元素的有限序列,对n>0,除第一个元素无直接前驱,最后一个元素无直接后继外,其余的每个数据元素只有一个直接前驱和一个直接后继。
线性表的特点:
1)同一性:
线性表有同类元素数据组成,每一个
必须属于同一数据类型。
2)有穷性:
线性表由有限个数据元素组成,表长就是表中数据元素的个数。
3)有序性:
线性表中相邻数据元素之间存在着序偶关系
。
2.1.2线性表的抽象数据类型定义
抽象数据类型定义:
见课本
。
2.2线性表的顺序存储
2.2.1线性表的顺序存储结构
顺序存储结构的定义
线性表的顺序存储结构是指用一组地址连续的存储单元依次存储线性表中的各个元素,使得线性表中在逻辑上相邻的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
采用顺序存储结构的线性表通常称为顺序表。
将线性表归纳为:
关系线性化,节点顺序存。
假定顺序表中有
个元素,每个元素占
个单元,第一个元素的地址为
,则可通过如下公式计算出第
个元素的地址
:
其中,
称为基地址。
线性存储结构的C语言定义
#defineMAXSIZE100
typedefstruct
{
ElemTypeelem[MAXSIZE];/*ElemType可为int,char等*/
intlast;/*记录线性表中最后一个元素在数组elem[]中的位置(下标值)*/
}Seqlist;
上述为定义一个结构体,结构名为Seqlist。
知识延伸(typedef)(C课本
用typedef声明新类型名)
2.2.2线性表顺序存储结构上的基本运算
线性表的基本运算
1、查找操作
2、插入操作
3、删除操作
4、顺序表合并算法
线性表顺序存储结构的优缺点分析
2.3线性表的链式存储
链表定义:
采用链式存储结构的线性表称为链表。
链表的分类:
1)按实现角度看:
链表可分为动态链表和静态链表。
2)按链接方式的角度看:
链表可分为单链表、循环链表和双链表。
2.3.1单链表
2.3.2单链表上的基本操作
线性表的基本运算
1、初始化单链表
InitList(LinkList*L)
{
*L=(LinkList)malloc(sizeof(Node));/*建立头结点*/
(*L)->next=NULL;/*建立空的单链表L*/
}
注意:
L是指向单链表的头结点的指针,用来接收主程序中带初始化单链表的头指针变量的地址,*L相当于主程序中带初始化单链表的头指针变量。
2、建立单链表
1)头插法建表
算法描述:
从一个空表开始,重复读入数据,生成新结点,将读入数据存放到新节点的数据域中,然后将新节点插入到当前链表的表头节点之后,直至读入结束标志为止。
单链表的存储结构:
typedefstructNode/*结点类型定义,结构体名为Node*/
{
ElemTypedata;
structNode*next;
}Node,*Linklist;/*LinkList为结构指针类型*/
LinkList与Node*同为结构指针类型,这两种类型是等价的。
通常习惯上用LinkList说明指针变量,强调他是某个单链表的头指针变量。
例如,使用定义LinkListL,则L为单链表的头指针,从而提高程序的可读性。
用Node*来定义指向单链表中节点的指针,例如,Node*p,则p为指向单链表中节点的指针变量。
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*Linklist;
voidCreatFromHead(LinkListL)
{
Node*s;
charc;
intflag=1;
while(flag)
{
c=getchar();
if(c!
='$')
{
s=(Node*)malloc(sizeof(Node));
s->data=c;
s->next=L->next;
L->next=s;
}
elseflag=0;
}
}
2)尾插法建表
算法描述:
头插法建立链表虽然简单,但生成的链表中结点的次序和输入的顺序相反。
若希望二者相同,可采用尾插法建表。
该方法是将新节点插到当前单链表的尾上。
为此需增加一个尾指针r,使之指向当前单链表的结尾。
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*Linklist;
voidCreatFromHead(LinkListL)
{
Node*s,*r;
r=L;
charc;
intflag=1;
while(flag)
{
c=getchar();
if(c!
='$')
{
s=(Node*)malloc(sizeof(Node));
s->data=c;
r->next=s;
r=s;
}
else
{
flag=0;
r->next=NULL;
}
}
}
3、单链表查找
1)按序号查找
算法描述:
设带头结点的单链表的长度为n,要查找表中第i个结点,则需要从单链表的头指针L出发,从头结点(L->next)开始顺着链域扫描,用指针p指向当前扫描到的结点,初值指向头结点,用j做计数器,累计当前扫描过的结点数(初值为0),当j=i时,指针p所指的结点就是要找的第i个结点。
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*Linklist;
Node*Get(LinkListL,inti)
{
intj=0;
Node*p;
p=L:
if(i<=0)returnNULL;/*找的序号太小*/
while((p->next!
=NULL)&&(j
{
p=p->next;
j++;
}
if(p->next==NULL)returnNULL;/*找不到i*/
else
returni;/*找到了i*/
}
2)按值查找
算法描述:
按值查找是指在单链表中查找是否有节点值等于e的结点,若有的话,则返回首次找到的其值等于e的结点的存储位置,否则返回NULL。
查找过程从单链表的头指针指向的头结点出发,顺着链逐个将结点的值和给定值e作比较。
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*Linklist;
Node*Locate(LinkListL,ElemTypekey)
{
Node*p;
p=L->next;
while(p!
=NULL)
{
if(p->data==key)
returnp;/*找到了key*/
p=p->next;
}
returnNULL;/*没有找到了key*/
}
4、单链表插入操作
问题要求:
在线性表的第
个位置之前插入一个新元素e。
算法思想:
查找:
在单链表中找到第i-1个结点并有指针pre指示。
申请:
申请新节点s,将其数据域的值置为e;
插入挂链:
通过修改指针域将新节点s挂入单链表L。
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*Linklist;
voidInslist(ElemTypee,inti,LinkListL)
{
Node*pre,*s;
intk;
if(i<=0)returnERROR;
pre=L;k=0;
while(pre!
=NULL&&k{
pre=pre->next;
k++;
}
if(k==i)
{
s=(Node*)malloc(sizeof(Node));
s->data=e;
s->next=pre->next;
pre->next=s;
returnOK;
}
else
{
printf("插入位置不合理");
returnERROR;
}
}
5、单链表删除
问题要求:
将线性表的第
个元素e删除
算法思想:
查找:
通过计数方式找到第i-1个结点并由指针pre指示;
删除第i个结点并释放结点空间。
结果:
将长度为n的线性表
变成长度为n-1
的线性表
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*Linklist;
voidDelList(LinkListL,inti,ElemType*e)
{
Node*pre,*s;
intk;
if(i<=0)returnERROR;
pre=L;k=0;
/*按序号查找第i个位置*/
while(pre->next!
=NULL&&k
{
pre=pre->next;
k++;
}
if(pre->next==NULL)
{
printf("删除位置错误");
returnERROR;
}
s=pre->next;/*使得删除得第i个位置的指针为s*/
pre->next=s->next;
*e=s->data;
free(s);/*释放被删除的结点所占的内存空间*/
returnOK;
}
算法应用举例
1、求单链表的长度
算法描述:
可以采用“数”结点的方法来求出单链表的长度,用指针p依次指向各个结点,从第一个元素开始“数”,一直“数”到最后一个结点(p->next=NULL).
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*LinkList;
intListLength(LinkListL)
{
Node*p;
intk=0;
p=L:
while(p->next!
=NULL)
{
p=p->next;
k++;
}
returnk;
}
2、求两个集合的差
已知:
以单链表表示集合,假设集合A用单链表LA表示,集合B用单链表LB表示,设计算法求两个集合的差,即A-B。
算法思想:
由集合运算的规则可知,集合的差A-B中包含所有属于集合A而不属于集合B的元素。
具体做法是,对于集合A中的每个元素e,在集合B的链表LB中进行查找,若存在与e相同的元素,则从LA中将其删除。
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*LinkList;
voidDifference(LinkListLA,LinkListLB)
{
Node*p,*q,*pre,*r;
pre=LA;
p=LA->next;
while(p!
=NULL)
{
q=LB->next;
while(p->data!
=q->data&&q!
=NULL)/*查找有无相同元素*/
q=q->next;
if(p->data==q->data)/*有相同元素*/
{
r=p;
pre->next=p->next;/*本来pre->next=p,现改为pre->next=p->next*/
p=p->next;/*下一次判断p->next*/
free(r);/*释放被删除的元素的空间*/
}
else
{
pre=p;/*也可写为pre=pre->next*/
p=p->next;
}
}
}
3、有两个单链表LA和LB,其元素均为非递减有序排列,编写一个算法,将他们合并成一个单链表LC,要求LC也是非递减有序排列。
要求:
新表LC利用现有的表LA和LB中的元素结点空间,而不要额外申请结点空间。
例如
,则
。
算法思想:
要求利用现有的表LA和LB中的结点空间来建立新表LC,可通过更改结点的next域来重建新的元素之间的线性关系。
为包证新表仍然递增有序,可以利用尾插法建立单链表的方法,只是新表中的结点不用malloc,而只要从表LA和LB中选择合适的点插入新表LC即可。
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*LinkList;
LinkListMergeLinkList(LinkListLA,LinkListLB)
{
Node*pa,*pb,*pc;
LinkListLC;
/*pa和pb分别指向两个单链表LA和LB中的将要判断
的结点,pc初值为LC且pc始终指向LC的表尾*/
pa=LA->next;
pb=LB->next;
LC=LA;
LC->next=NULL;/*将LC初始置为空表*/
pc=LC;/*pc始终指向LC的表尾*/
/*当两表均未处理完时,选择较小者*/
while(pa!
=NULL&&pb!
=NULL)
{
if(pa->data<=pb->data)
{pc->next=pa;pc=pc->next;pa=pa->next;}
else
{pc->next=pb;pc=pc->next;pb=pb->next;}
}
if(pa==NULL)/*表B未处理完*/
pc->next=pb;
else/*表A未处理完*/
pc->next=pa;
free(LB);/*表C是以表A为基础,所以释放表B*/
returnLC;
}
2.3.3循环链表
循环链表:
循环链表是一个首尾相接的链表。
特点:
将单链表最后一个结点的指针域由NULL改为指向头结点或线性表中的第一个结点,就得到了单链表形式的循环链表,并称为循环单链表。
在循环单链表中,表中所有结点被链在一个环上。
带头结点的循环链表的算法和带头结点的单链表的算法的区别仅在与算法中判别当前结点p是否为尾结点。
单链表判别条件为
,而循环单链表的判别条件为
。
例题:
有两个带头结点的循环单链表LA、LB,编写算法,将两个循环单链表合并为一个循环单链表,其头指针为LA。
算法思想:
先找到两个链表LA、LB的表尾,并分别由指针p,q指向它们,然后将第一个链表的尾与第二个链表的第一个结点链接起来,并修改第二表的表尾q,使它指向第一个表的头结点。
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*LinkList;
LinkListmerge_1(LinkListLA,LinkListLB)
{
Node*p,*q;
p=LA;q=LB;
/*寻找A的尾结点,并置为p*/
while(p->next!
=LA)
p=p->next;
/*寻找B的尾结点,并置为q*/
while(q->next!
=LB)
q=q->next;
/*修改A的尾指针,并使之为B的第一节点*/
p->next=LB->next;
/*修改B的尾指针,并使之为A的头结点*/
q->next=LA;
free(LB);
returnLA;
}
采用上面的方法,需要遍历链表,找到表尾,其执行时间为
。
若循环单链表设置为尾指针表示,在实现上述合并时,无需循环遍历找尾结点,只需直接修改尾结点的指针域,其执行时间是
。
算法:
typedefstructNode
{
ElemTypedata;
structNode*next;
}Node,*LinkList;
LinkListmerge_2(LinkListRA,LinkListLB)
{
Node*p;
p=RA->next;/*记录A的头结点*/
RA->next=RB->next->next;
free(RB->next);
RB->next=p;
returnRB;/*返回新循环链表的尾指针*/
}
2.3.4双向链表
双向链表:
给单链表的每个结点里再增加一个指向其前驱的指针域prior。
双链表的结点的结构如下图:
prior
data
next
双链表的结构定义如下:
typedefstructDNode
{
ElemTypedata;
structDNode*prior,*next;
}DNode,*DoubleList;
设指针p指向双链表中某一点,则有下式成立:
1、双向链表的前插操作
算法思想:
欲在双向链表第i个结点之前插入一个新的结点,则指针的变化情况如下图:
算法:
typedefstructDNode
{
ElemTypedata;
structDNode*prior,*next;
}DNode,*DoubleList;
intDlinkIns(DoubleListL,inti,ElemTypee)
{
DNode*p,*s;
intk;
if(i<=0)
returnERROR;
p=L;k=0;
while(p!
=NULL&&k
{
p=p->next;
k++;
}
if(p==NULL)
{
printf("插入位置不合理");
returnERROR;
}
s=(DNode*)malloc(sizeof(DNode));
s->data=e;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
returnOK;
}
2、双向链表的删除操作
算法思想:
欲删除双向链表中的第i个结点,则指针的变化情况如下图所示:
算法:
typedefstructDNode
{
ElemTypedata;
structDNode*prior,*next;
}DNode,*DoubleList;
intDlinkDel(DoubleListL,inti,ElemType*e)
/*将删除的元素保存到*e中*/
{
DNode*p;
intk;
if(i<=0)
returnERROR;
p=L;k=0;
while(p!
=NULL&&k
{
p=p->next;
k++;
}
if(p==NULL)
{
printf("插入位置错误");
returnERROR;
}
*e=p->data;
p->prior->next=p->next;
p->next=p->prior;
free(p);
returnOK;
}
3、应用举例
例题:
已知:
设一个循环双链表
编写一个算法将链表转换为
。
算法思想:
实际上是交换表中前两个元素的次序。
算法:
typedefstructDNode
{
ElemTypedata;
structDNode*prior,*next;
}DNode,*DoubleList;
voidswep(DLinkListL)
{
DNode*p,*q,*r;
p=L->next;
q=p->next;
r=p->prior;
p->next=q->next;
q->next->prior=p;
p->prior=q;
q->next=p;
q->prior=r;
L->next=q;
returnL;
}
2.4一元多项式的表示及相加
2.4.1一元多项式的表示
一元多项式可按升幂的形式写成:
其中,
为第
项的指数,
是指数
的项的系数,且
。
假设
是一个一元多项式,则它可以用一个线性表
来表示。
即:
若假设
,则两个多项式相加的结果
,也可以用线性表
来表示:
2.4.2一元多项式的存储
1、一元多项式的顺序存储表示
2、一元多项式的链式存储表示
在链式存储中,对一元多项式只存非零项,则该多项式中每一项由两部分组成(指数项和系数项),用单链表存储表示的结点结构如下图:
系数coef
指数exp
指针next
结点结构定义如下:
typedefstructPolynode
{
intcoef;
intexp;
structPolynode*next;
}Polynode,*Polylist;
建立多项式链表:
算法描述:
通过键盘输入一组多项式的系数和指数,用尾插法建立一元多项式链表。
以输入系数0为结束标志,并约定建立多项式链表时,总是按指数由小到大的顺序排列。
算法:
typedefstructPolynode
{
intcoef;
intexp;
Polynode*next;
}Polynode,*Polylist;
PolylistPolyCreate()
{
Polynode*head,*rear,*s;
intc,e;
/*申请头结点*/
head=(Polynode*)malloc(sizeof(Polynode));
rear=head;/*rear始终指向最后一个结点*/
scanf("%d%d",&c,&e);
while(c!
=0)
{
/*申请一个新的结点*/
s=(Polynode*)malloc(sizeof(Polynode));
s->coef=c;
s->exp=e;
rear->next=s;/*将链表连接起来*/
rear=s;/*链表的结尾后移一位*/
scanf("%d%d",&c,&e);
}
rear->next=NULL;/*结束链表*/
returnhead;/*返回链表的头结点*/
}
3、两个多项式相加
运算规