严蔚敏数据结构讲义第02章 线性表.docx
- 文档编号:9017819
- 上传时间:2023-02-02
- 格式:DOCX
- 页数:27
- 大小:588.01KB
严蔚敏数据结构讲义第02章 线性表.docx
《严蔚敏数据结构讲义第02章 线性表.docx》由会员分享,可在线阅读,更多相关《严蔚敏数据结构讲义第02章 线性表.docx(27页珍藏版)》请在冰豆网上搜索。
严蔚敏数据结构讲义第02章线性表
第02章线性表
本章知识结构:
线性表的特点:
1.有唯一的头;
2.唯一的尾;
3.除头外都有一个直接前驱;
4.除尾外都有一个直接后继。
2.1线性表的类型定义
一、线性表是n(n>=0)个数据元素的有限序列——有限个元素;元素之间有次序。
长度:
表中元素的个数。
存储容量:
整个线性表所占的空间。
位序:
元素所在位置的序号,即第几个。
线性表的基本操作:
1.访问(元素查询,定位);2.插入元素;3.删除元素。
线性表的抽象数据类型定义(三元法定义)ADTList{数据对象;数据关系;基本操作}
2.2顺序表(sequence)——线性表的顺序表示与实现
一、特点:
1.逻辑地址相邻,物理地址也相邻;
2.优点:
可随机访问;
缺点:
插入、删除不方便。
二、实现:
一维数组
三、C语言知识复习
(一)typedefintDataType;给整型int定义一个别名DataType,之后就可以使用DataType定义整型变量了,例如:
DataTypex,y=8;
(二)结构体类型的定义
structcard{intnum;charname[20];……}——定义了一个结构体card
(三)结构体变量的定义
structcardstu1,stu2;——其中structcard是一个整体,表示结构体类型名
(四)给结构体类型名定义别名
typedefstructcardDataType;给结构体structcard定义一个别名DataType
(五)给匿名结构体定义别名
typedefstruct
{intmonth;
intday;
intyear;}Date;//该结构体为匿名结构体,相当于类的匿名对象。
即该结构体无名称,只有别名
classPC;newPC().action();//匿名对象的使用
(六)sizeof函数:
返回一个对象或者类型所占的内存字节数。
如:
sizeof(int)——2
(七)用typedef定义指针型别名
typedefchar*String;//声明String为字符指针类型
typedefstructnode{……}*LinkList;//声明LinkList为structnode类型的指针
(八)malloc函数和free函数——memoryallocation内存分配
1.函数原型及说明:
void*malloc(longNumBytes):
该函数分配了NumBytes个字节,并返回了指向这块内存的指针。
如果分配失败,则返回一个空指针(NULL)。
关于分配失败的原因,应该有多种,比如说空间不足就是一种。
voidfree(void*FirstByte):
该函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。
2.函数的使用:
char*Ptr=NULL;
Ptr=(char*)malloc(100*sizeof(char));
//malloc()函数的类型是(void*),任何类型的指针都可以转换成(void*),但是最好还是在前面进行强制类型转换
(九)calloc函数
功能:
在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。
跟malloc的区别:
calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据。
(一十)realloc函数
原型:
externvoid*realloc(void*mem_address,unsignedintnewsize);
语法:
指针名=(数据类型*)realloc(要改变内存大小的指针名,新的大小)。
头文件:
#include
功能:
先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域,同时返回新分配的内存区域的首地址。
即重新分配存储器块的地址。
返回值:
如果重新分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
注意:
这里原始内存中的数据还是保持不变的。
当内存不再使用时,应使用free()函数将内存块释放。
四、线性表的结构定义——存储结构
typedefstruct{
ElemTpye*elem;//存储空间首地址
intlength;//存储长度
intListSize;//存储容量
}
五、线性表的插入
1.检查i值是否超出所允许的范围(1≤i≤n+1),若超出,则进行“超出范围”错误处理;
2.检查容量是否已满,若已满则需扩充存储容量;
3.将线性表的第i个元素和它后面的所有元素均向后移动一个位置;
4.将新元素写入到空出的第i个位置上;
5.使线性表的长度增1。
六、顺序表的删除
七、顺序表的查找
八、线性表合并
九、顺序存储优缺点
2.3链表——线性表的链式表示与实现
一、线性链表/单链表
(一)结点结构定义
(二)基本运算
1.建单链表
(1)头插法建单链表
ListLinkHeadInsertCreateList()//头插法创建单链表
{
ListLinkhead,s;
charch;
head=NULL;
printf("Pleaseinputcharacter(#->stop).\n");
ch=getchar();
while(ch!
='#')
{
s=(ListLink)malloc(sizeof(ListNode));//申请空间
s->data=ch;//结点数据域赋值
s->next=head;//将新节点的指针域指向头指针(即指向第一个数据元素地址)
head=s;//将头指针指向新结点
ch=getchar();//继续接收键盘字符,循环创建数据元素
}
returnhead;
}
(2)尾插法建单链表
ListLinkTailInsertCreateList()//尾插法创建单链表
{
ListLinkhead,tail,s;//head头指针;tail尾指针;s临时指针
charch;
head=NULL;
tail=head;//起始时,单链表无数据元素,头指针和尾指针都为空
printf("Pleaseinputcharacter(#->stop).\n");
ch=getchar();
while(ch!
='#')
{
s=(ListLink)malloc(sizeof(ListNode));//申请空间
s->data=ch;//结点数据域赋值
s->next=NULL;//新结点作为最后一个结点出现,所以将新结点的指针域置空
if(tail==NULL)//尾结点为空,此时插入第一个元素,应将头指针指向之,之后头指针保持不变
{
head=s;
tail=s;//第一个元素进入,尾指针也要指向第一个元素
}
else
tail->next=s;//第个及以后的元素插入都是直接将元素链入尾部
tail=s;//将尾指针指向新结点
ch=getchar();//继续接收键盘字符,循环创建数据元素
}
returnhead;
}
2.查找
(1)按值查找
linklist*LOCATE(linklist*head,datatypekey)
{linklist*p;
p=head->next;//头结点不属于表
while(p!
=NULL)
if(p->data!
=key)
p=p->next;
elsebreak;
returnp;
}
(2)按位置(序号)查找——可拓展到遍历
linklist*GET(linklist*head,inti)
{intj;
linklist*p;
p=head;j=0;
while((p->next!
=NULL)&&(j
{p=p->next;
j++;
}
if(i==j)returnp;
elsereturnNULL;
}
3.插入元素
(1)单个结点后插操作
//后插元素,L为单链表头指针,在q所指向的元素后面插入一个元素,值为e
ListLinkListInsertAfter(ListLinkL,ListLinkp,DataTypee)
{
ListLinkq=(ListLink)malloc(sizeof(ListNode));
if(q==NULL)
{
printf("MemoryallocationError.\n");
exit(0);
}
q->data=e;
q->next=p->next;
p->next=q;
returnL;
}
(2)单个结点前插操作
InsertBefore(linklist*head,linklist*p,datatypex)
{linklist*s,*q;
s=malloc(sizeof(linklist));
s->data=x;
q=head;
while(q->next!
=p)q=q->next;
s->next=p;q->next=s;
}
4.删除元素
(1)删除后继结点
二、
按位置查找(即访问第i个元素)的时间复杂度是
;——Addi=Add0+(i-1)*d
按值查找(即查找值为x的元素),需要比较(n+1)/2次,时间复杂度为
;
插入元素需要平均移动节点n/2个;
——设线性表有n个元素,如果要在第1个元素位置插入元素,则需要移动n个元素;如果要在第2个元素位置插入,则需要移动n-1个元素…,如果要在第n个元素位置插入需要移动1个;如果要在最末尾插入元素,则不需要移动元素,即移动0个元素,列出所有要移动元素的和,n+(n-1)+(n-2)+…+1+0,最终的n(n+1)/2。
插入总次数为n+1,所以移动平均数为[n(n+1)/2]÷(n+1)=n/2。
删除元素的平均时间复杂度为(n-1)/2。
链式存储:
查找(访问)第i个元素的时间复杂度是
;——从第1个找第2个,再通过第2个找第3个,…
插入元素的平均时间复杂度为
;
删除元素的平均时间复杂度为
。
二、静态链表与动态链表
静态链表:
所有结点都是在程序中定义,不是临时开辟的,也不能用完后释放。
静态链表中能容纳的元素个数的最大数在表定义时就确定了,以后不能增加。
静态链表使用结构体数组来实现线性链表的功能。
因为其用游标cur来指示下一个数据元素的存储位置,因此存取数据时静态链表与线性链表(单链表)是相似的。
线性链表在存取第i个元素时,是先找到第一个元素,然后根据链接依次找到第2个、第3个…,直到找到第i个,所以静态链表在存取表中第i个元素的时间同i有关。
动态链表:
在需要时才开辟一个结点的存储单元。
无论是静态链表还是动态链表,都是链式存储,所以静态链表和动态链表在元素的插入、删除操作上类似,不需要做元素的移动。
三、线性表查找元素的方法通常有顺序查找和折半查找两种。
顺序查找适合于顺序存储和链式存储;折半查找只适合于顺序存储。
四、线性表的头指针、头结点、表头结点
表头结点表示线性表中第一个存储数据的结点,即第一个数据元素的结点。
线性表有的有头结点,有的没有头结点,头结点是在线性表第一个数据元素结点的前面再加一个结点,该结点同数据结点结构相同,但是不存储数据,只是用来标识线性表的起始位置。
如果线性表有头结点,则指向头结点的指针叫头指针;如果线性表没有头结点,则指向表头结点的指针叫头指针。
第一章栈、队列和数组
一、用单链表表示栈,总是链表头对应栈的栈顶,链表尾对应栈的栈尾;
用单链表表示队列时,用链表头对应队列的队头,链表尾对应队列的队尾。
二、对称矩阵aij进行压缩存储,如果按行存储下三角阵,则LOC(i,j)的存放位置:
LOC(i,j)=
(i≥j,即元素位于下三角时)
LOC(i,j)=
(i<j,即元素位于上三角时)
三、使用循环队列来判断一个队列是空是满(队头front,队尾rear,队列最大容量为max):
判断栈满:
front==(rear+1)%max
判断栈空:
front==rear——>栈空
计算队列中元素的个数:
(rear–front+max)%max
四、后缀表达式
前序遍历是前缀式==波兰式
中序遍历是中缀式
后续遍历是后缀式==逆波兰式
第二章树和二叉树
一、树的基本概念
1.结点的度:
结点的分支数,即结点分叉的总数(不含父叉)。
2.结点的层次(高度):
结点处在树的第几层,根结点的层次是1。
3.树的度:
树中所有结点度的最大值——即最茂密的那个结点的分叉数。
4.树的深度(高度):
树中所有结点层次的最大值——树枝的最大深度。
二、二叉树
(一)属性
1.二叉树第i层上至多有2i-1个结点。
2.深度为k的二叉树至多有2k-1个结点,最少有k个结点。
3.二叉树叶子结点数总比度为2的结点个数多1。
——
4.二叉树的度最多为2,但不一定为2,有可能为0(只含一个根结点的树),也有可能为1(一个根结点只含左子树,无右子树)。
(二)存储结构
1.顺序存储:
将二叉树补成完全二叉树(无结点的地方补0),然后按层次存储。
2.链式存储
(1)二叉链表:
数据域;左指针指向左子树;右指针指向右子树。
方便找子树,不方便找双亲。
(2)三叉链表:
数据域;左指针指向左子树;右指针指向右子树;双亲指针指向双亲。
(三)二叉树的遍历——先序、中序和后序,都是以根结点出现的次序而言的。
先序:
先根,再左,再右。
——前缀(波兰式)
中序:
先左,再根,再右。
——中缀
后序:
先左,再右,再根。
——后缀(逆波兰式)
1.一个前序遍历+一个后序遍历不能唯一确定一个二叉树。
2.一个中序遍历+一个前序遍历能唯一确定一棵二叉树;一个中序遍历+一个后序遍历也能唯一确定一棵二叉树。
(即只要是含有中序遍历,即可确定一棵二叉树)。
3.无论先序、中序还是后序遍历,二叉树叶子结点的相对排列顺序不变,因为都是先左后右。
(四)线索二叉树
Tag为0表示相应的Child指针指向子树结点;如果Tag为1则表示指针指向前驱或后继结点。
LChild
LTag
Data
RTag
RChild
三、特殊二叉树(满二叉树和完全二叉树)
(一)满二叉树
深度为k的满二叉树,第k层的结点数为2k-1,总结点数为2k-1。
(二)完全二叉树
定义:
(1)叶子结点只能出现在树的最高两层
(2)树的层次排列无间断编号。
1.深度为k的完全二叉树最多结点数为2k-1(满二叉树),最少为2k-1(深度为k-1的满二叉树+1)。
2.满二叉树为完全二叉树。
3.具有n个结点的完全二叉树的深度为
+1。
4.完全二叉树的n0=n2+1。
5.完全二叉树的n1=0或n1=1。
6.完全二叉树的总结点数n=n0+n1+n2=2n0-1+n1=2n1+1+n1(=2n0或2n0-1;=2n1+1或2n1+2)
四、树和森林
(一)树的存储结构
1.双亲表示法:
顺序存储,便于查找双亲。
2.孩子表示法:
顺序+链式
3.孩子兄弟表示法:
二叉链表,左指第一个孩子,右指第一个兄弟。
(二)森林和二叉树的相互转换
1.一般树转化为二叉树:
将树按孩子兄弟法表示,左子树只保留父亲和长子关系,右子树是年龄最近的两个兄弟的连线。
一般树转化为二叉树之后,根结点没有右子树。
2.森林转换为二叉树
先将森林中的各树转化为二叉树,然后把第1棵树的根结点作为总二叉树的根结点,把第2棵树的根结点作为第1棵树根结点的右子树,把第3棵树的根结点作为第2棵树的右子树……
(1)森林转换为二叉树,则森林中第1棵树即变成二叉树根结点的左子树;其余树合起来变成二树根结点的右子树;可如此推演,第2棵树是整个二叉树根结点的右子树中的左子树……
3.二叉树转化为森林
将二叉树根结点的右子树的每个分叉的根结点之间的连线都断开,即成森林。
(三)树和森林的遍历
1.树的遍历
(1)先根遍历:
先访问树的根结点,再依次先根访问树的各子树。
树的先根遍历得到的序列与对应的二叉树的先根遍历序列相等。
(2)后根遍历:
依次后根遍历各子树,最后访问根结点。
树的后根遍历得到的序列与相应的二叉树的中序序列相等。
2.森林的遍历
(1)先序遍历:
依次从左至右对森林中的每一棵树进行先根遍历。
(2)中序遍历:
依次从左至右对森林中的每一棵树进行后根遍历。
五、树的应用
(一)二叉排序树:
左小右大根中间,二叉排序树的中序遍历序列即是元素的有序序列(自小到大或自大到小)
平均查找长度:
ASL=
比如,一个有12个元素的序列,生成二叉树之后,根结点有20=1个元素;第2层,有22-1=2个元素;第3层有23-1=4个元素;在第4层有12-(1+2+4)=5个元素,则其ASL=(1*1+2*2+3*4+4*5)/12=37/12
(二)平衡二叉树——排序树,是二叉查找树的另一种形式
平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
(三)哈夫曼树和哈弗曼编码
1.结点之间的路径长度:
树上结点之间的分支数目(即从A点到B点,需要走几个树枝)。
2.树的路径长度:
从树的根结点到每一个终端结点的路径长度之和。
3.结点的带权路径长度:
从树的根到该终端结点的路径长度与结点上权值的乘积。
4.树的带权路径长度:
树中所有终端结点的带权路径长度之和。
5.哈夫曼树:
带权路径长度最小的二叉树称为最优二叉树,即哈夫曼树。
6.构造哈夫曼树的步骤:
——哈夫曼树没有度为1的结点,所以n=2n0-1。
7.哈夫曼编码:
哈夫曼树上左0右1,从根到结点的树枝对应的0、1所形成的编码。
第三章图
一、图的基本概念
(一)图的定义及术语
1.图的定义
(1)图:
图是若干顶点和若干边的集合。
若只有顶点而无边,则称顶点离散;有边则称顶点关联。
(2)顶点:
图中的数据元素。
(3)边:
若顶点u和顶点v之间有关联(即有连线),且连线无方向性,则称(u,v)为边。
(4)弧:
若顶点u和顶点v之间有关联(即有连线),且连线有方向性,则称或
(5)弧头:
弧指向的顶点(和箭头相类似),也称弧的终端点——中,v是弧头。
(6)弧尾:
弧起始的顶点,也称弧的初始点——中,u是弧尾。
(7)无向图:
图中顶点之间的关联无方向性(即顶点之间由边相连)的图。
(8)有向图:
图中顶点之间的关联有方向性(即顶点之间由弧相连)的图。
(9)完全图:
任意两个顶点之间都有边的无向图(
)。
(10)有向完全图:
任意两个顶点之间都有两条弧相连的有向图(
)。
2.图中边(弧)与图的顶点的关系
(1)无向图中边e和顶点n之间的关系:
(2)有向图中边e和顶点n之间的关系:
(3)稀疏图、稠密图:
有很少边或者弧的图(
)叫稀疏图;反之称为稠密图。
3.权:
边或者弧上的数称为权,表示从一个顶点到另一个顶点的距离或花费。
4.子图:
从一个图中选其部分顶点及这些顶点之间的关联(边或弧)组成的新图称为原图的子图。
5.度:
和顶点v相关联的边或弧的条数,称为顶点v的度,记作:
TD(v)。
6.有向图中顶点v的度:
和顶点v有关联的弧的数量。
这些弧包括v指向其他顶点的弧,也包括其他顶点指向v的弧。
7.出度OD(v):
由顶点v射向其他顶点的弧的总条数。
8.入度ID(v):
射向v的弧的总条数。
9.图中所有顶点度的和与顶点、边、弧的关系:
(1)无向图中所有顶点度的和,是图中所有边的总数的2倍。
(因为一个边对应两个度)
(2)有向图中所有顶点度的和,是图中所有弧的总数的2倍。
(因为一个弧也对应两个度,一个出度,一个入度,所以对有向图有:
所有顶点出度数总和=所有顶点入度数总和=图中弧的总数=
(二)图的连通性
1.路径和回路
(1)路径:
顶点Vi走到Vj所经历的顶点序列(包括Vi和Vj)。
(2)有向路径:
在有向图中顶点Vi走到Vj所经历的顶点序列(包括Vi和Vj)。
(3)简单路径:
路径上没有重复顶点的路径。
(4)路径长度:
路径中边或者弧的数量即为路径长度。
(5)回路/环路:
路径的起始顶点=路径的重点顶点,则称该路径为回路或环路。
(6)简单回路/简单环:
除第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路。
(7)非简单回路:
除第一个顶点和最后一个顶点之外,其余顶点有重复出现的回路。
2.图的连通性
(1)顶点之间连通:
若顶点v和u之间有路径(即由v到u可达),则称v和u是连通的。
(2)连通图:
图中任何两个顶点之间都连通。
n个顶点的无向图,要想达到连通,最少需要n-1条边,此时图为树状结构;
n个顶点的有向图,要想达到连通,最少需要n条边,此时图为环状结构。
(3)强连通图:
有向图的任何两个顶点正向连通,逆向也连通,则称该有向图为强连通图。
(4)连通分量和极大连通子图
连通分量:
无向图的最大连通子图,可能含有回路——“最大”指的是依附于连通分量中顶点的所有边都加上。
强连通分量:
有向图的极大强连通子图称为其强连通分量。
极大连通子图:
3.连通图的生成树问题
连通图的生成树就是一棵树,是一没有环路的连通图。
生成树若有n个顶点,则其边定为n-1个。
二、图的存储及基本操作
(一)邻接矩阵法——顺序存储,用n*n的矩阵A来保存图
1.表示方法
对无向图:
vi,vj之间有边,则aij=aji=1,否则aij=aji=0,为对称矩阵。
对有向图:
vi向vj有弧,则aij=1,否则aij=0,不一定是对称矩阵,除非两节点之间是双向连通的。
对带权图:
vi,vj之间有边(弧),则aij=相应的权,否则aij=INFINITY
2.相关求解
(1)判断vi和vj之间是否有边(弧),看aij的值是1还是0。
(2)求无向图顶点vi的度:
第i行或第i列元素之和。
(3)求有向图顶点vi的出度:
第i行元素之和。
求有向图顶点vi
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 严蔚敏数据结构讲义第02章 线性表 严蔚敏 数据结构 讲义 02 线性