严蔚敏数据结构讲义第02章 线性表.docx

上传人:b****6 文档编号:9017819 上传时间:2023-02-02 格式:DOCX 页数:27 大小:588.01KB
下载 相关 举报
严蔚敏数据结构讲义第02章 线性表.docx_第1页
第1页 / 共27页
严蔚敏数据结构讲义第02章 线性表.docx_第2页
第2页 / 共27页
严蔚敏数据结构讲义第02章 线性表.docx_第3页
第3页 / 共27页
严蔚敏数据结构讲义第02章 线性表.docx_第4页
第4页 / 共27页
严蔚敏数据结构讲义第02章 线性表.docx_第5页
第5页 / 共27页
点击查看更多>>
下载资源
资源描述

严蔚敏数据结构讲义第02章 线性表.docx

《严蔚敏数据结构讲义第02章 线性表.docx》由会员分享,可在线阅读,更多相关《严蔚敏数据结构讲义第02章 线性表.docx(27页珍藏版)》请在冰豆网上搜索。

严蔚敏数据结构讲义第02章 线性表.docx

严蔚敏数据结构讲义第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有些编译器需要#include,在TC2.0中可用alloc.h头文件

功能:

先按照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模板 > 国外设计风格

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

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