软考软件设计师专题九数据结构知识文档格式.docx
《软考软件设计师专题九数据结构知识文档格式.docx》由会员分享,可在线阅读,更多相关《软考软件设计师专题九数据结构知识文档格式.docx(55页珍藏版)》请在冰豆网上搜索。
顺序映象和非顺序映象,并由此得到两种不同的存储结构:
顺序存储结构和链式存储结构。
任何一个算法的设计取决于选定的数据<
逻辑)结构,而算法的实现依赖于采用的存储结构。
数据的逻辑结构分为两类:
线性结构:
线性表、栈、队列和串
非线性结构:
树、图
数据的存储方法有四类:
顺序存储方法
链接存储方法
索引存储方法
散列存储方法
2.常用数据结构
2.1线性表
在数据结构中,线性结构常称为线性表,是最简单、最常用的一种数据结构,它是由n个相同数据类型的结点组成的有限序列。
其特点是:
在数据元素的非空有限集合中,
◆存在唯一的一个被称做“第一个”的数据元素
◆存在唯一的一个被称做“最后一个”的元素数据元素
◆除第一个之外,集合中的每个数据元素均只有一个前驱
◆除最后一个之外,集合中每个数据元素均只有一个后继
一个由n个结点e0,e1…,en-1组成的线性表记为:
<
e0,e1…,en-1)。
线性表的结点个数称为线性表的长度,长度为0的线性表称为空的线性表,简称空表。
对于非空线性表,e0是线性表的第一个结点,en-1是线性表的最后一个结点。
线性表的结点构成了一个序列,对序列中两个相邻结点ei和ei-1,称前者是后者的前驱结点,后者是前者的后继结点。
线性表最重要的性质是线性表中结点和相对位置是确定的。
线性表的结点也称为表元,或称为记录,要求线性表的结点一定是同一类型的数据。
线性表的结点可由若干个成分组成,其中唯一标识表元的成分成为关键字,简称键。
线性表是一个相当灵活的数据结构,它的长度可以根据需要增长或缩短。
对线性表的基本运算如下:
INITIATE<
L)初始化操作
LENGTH<
L)
求长度函数
GET<
L,i)
取元素函数
PRIOR<
L,elm)求前驱函数
NEXT<
L,elm)求后继函数
LOCATE<
L,x)定位函数
INSERT<
L,i,b)插入操作
DELETE<
L,i)
删除操作
有多种存储方式能将线性表存储在计算机内,其中最常用的是顺序存储和链接存储。
根据存储方式的不同,其上述的运算实现也不一样。
◆顺序存储:
是最简单的存储方式,其特点是逻辑关系上相邻的两个元素在物理位置上也相邻。
通常使用一个足够大的数组,从数组的第一个元素开始,将线性表的结点依次存储在数组中。
顺序存储方式优点:
能直接访问线性表中的任意结点。
线性表的第i个元素a[i]的存储位置可以使用以下公式求得:
LOC<
ai)=LOC<
a1)+<
i-1)*l
式中LOC<
a1)是线性表的第一个数据元素a1的存储位置,通常称做线性表的起始位置或基地址。
顺序存储的缺点:
1>
线性表的大小固定,浪费大量的存储空间,不利于节点的增加和减少;
2>
执行线性表的插入和删除操作要移动其他元素,不够方便;
◆链式存储
线性表链接存储是用链表来存储线性表。
单链表<
线性链表):
从链表的第一个表元开始,将线性表的结点依次存储在链表的各表元中。
链表的每个表元除要存储线性表结点的信息以外,还要有一个成分来存储其后继结点的指针。
线性链表的特点是:
每个链表都有一个头指针,整个链表的存取必须从头指针开始,头指针指向第一个数据元素的位置,最后的节点指针为空。
当链表为空时,头指针为空值;
链表非空时,头指针指向第一个节点。
链式存储的缺点:
由于要存储地址指针,所以浪费空间;
直接访问节点不方便;
3>
4>
5>
6>
7>
循环链表:
循环链表是另一种形式的链式存储结构,是单链表的变形。
它的特点就是表中最后一个结点的指针域指向头结点,整个链表形成一个环。
因此,从表中任意一个结点出发都可以找到表中的其他结点。
循环链表和单向链表基本一致,差别仅在于算法中循环的条件不是结点的指针是否为空,而是他们的指针是否等于头指针,
循环链表最后一个结点的link指针不为0(NULL>
,而是指向了表的前端。
为简化操作,在循环链表中往往加入表头结点。
循环链表的特点是:
只要知道表中某一结点的地址,就可搜寻到所有其他结点的地址。
循环链表的示例:
带表头结点的循环链表:
双向链表:
双向链表是另一种形式的链式结构,双向链表的结点中有两个指针域,其一指向直接后继,另一指向直接前趋。
双向链表克服了单链表的单向性的缺点。
前驱方向
后继方向
双向链表也可以有循环表,链表中存在两个环。
一个结点的前趋的后继和该结点的后继的前趋都是指向该结点的。
p==p→lLink→rLink==p→rLink→lLink
2.2栈
栈<
Stack)是限定仅在表尾进行插入或删除操作的线性表。
表尾端称栈顶<
top),表头端称栈底<
bottom)。
若有栈
S=<
s0,s1,…,sn-1)则s0为栈底结点,sn-1为栈顶结点。
通常称栈的结点插入为进栈,栈的结点删除为出栈。
因为最后进栈的结点必定最先出栈,所以栈具有后进先出的特点。
可以用一下一个图形来形象的表示:
栈有两种存储结构:
顺序栈和链栈
顺序栈即栈的顺序存储结构是,利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素,同时设指针top指示栈顶元素的当前位置。
栈也可以用链表实现,链式存储结构的栈简称链栈。
若同时需两个以上的栈,则最好采用这种结构。
对于栈上的操作,总结如下,大家可以仔细看一下这些程序,一个大的程序都是由一些对数据结构的小的操作组成的。
顺序存储的栈的基本操作如下:
判断栈满:
intstackfull(seqstack*s>
{
return(s->
top==stacksize-1>
。
}
进栈:
void
push(seqstack*s,datatype
x>
{
if(stackfull(s>
>
error(“stackverflow”>
s->
data[++s->
top]=x。
判断栈空:
intstackempty(seqstack*s>
top==-1>
出栈:
datatypepop(seqstack*s>
if(stackempty(s>
error(“stackunderflow”>
x=s->
data[top]。
s->
top--。
return(x>
链接存储栈:
用链表实现的栈,链表第一个元素是栈顶元素,链表的末尾是栈底节点,链表的头指针就是栈顶指针,栈顶指针为空则是空栈。
若同时需要两个以上的栈,最好采用链表作存储结构
链接存储的栈的操作如下:
Voidpush(linkstack*p,datatypex>
{
stacknode*qq=(stacknode*>
malloc(sizeof(stacknode>
q–>
data=x。
next=p–>
top。
p–>
top=q。
Datatype
pop(linkstack
*p>
datatype
x。
stacknode
*q=p–>
if(stackempty(p>
error(“stackunderflow.”>
x=q–>
data。
top=q–>
next。
free(q>
returnx。
多栈处理栈浮动技术:
n个栈共享一个数组空间V[m]
n设立栈顶指针数组t[n+1]和栈底指针数组b[n+1]
,t[i]和b[i]分别指示第i个栈的栈顶与栈底,b[n]作为控制量,指到数组最高下标
各栈初始分配空间s=m/n指针初始值
t[0]=b[0]=-1
b[n]=m-1
t[i]=b[i]=b[i-1]+s,
i=1,2,…,n-1
2.3队列
队列是只允许在一端进行插入,另一端进行删除运算的线性表。
允许删除的那一端称为队首<
front),允许插入运算的另一端称为队尾<
rear)。
通常称队列的结点插入为进队,队列的结点删除为出队。
若有队列
Q=<
q0,q1,…,qn-1)则q0为队首结点,qn-1为队尾结点。
因最先进入队列的结点将最先出队,所以队列具有先进先出的特性。
可以用顺序存储线性表来表示队列,也可以用链表实现,用链表实现的队列称为链队列。
队列操作:
①intpush(PNODE*top,inte>
是进栈函数,形参top是栈顶指针的指针,形参e是入栈元素。
②intpop(PNODE*top,intoe>
是出栈函数,形参top是栈顶指针的指针,形参e作为返回出栈元素使用。
③intenQueue(PNODE*tail,inte>
是入队函数,形参tail是队尾指针的指针,形参e是入队元素。
④intdeQueue(PNODE*tail,int*e>
是出队函数,形参tail是队尾指针的指针,形参e作为返回出队元素使用。
定义结点的结构如下:
typedefstructnode{
intvalue。
structnode*next。
}NODE,*PNODE。
[函数①]
intpush(PNODE*top,inte>
PNODEp=(PNODE>
malloc(sizeof(NODE>
if(!
p>
return–1。
p->
value=e。
next=*top。
//指向栈顶指针
*top=p。
return0。
[函数②]
intpop(PNODE*top,int*e>
PNODEp=*top。
if(p==NULL>
*e=p->
value。
*top=p->
//栈顶指向取出的数的指针
free(p>
[函数③]
intenQueue(PNODE*tail,inte>
PNODEp,t;
t=*tail。
p=(PNODE>
;
return–l。
p—>
next=t—>
t->
next=p。
//将元素加在尾指针后
*tail=p。
[函数④]
intdeQueue(PNODE*tail,inte>
PNODEp,q;
if((*tail>
->
next==*tail>
//队列已经空
p=(*tail>
//p获得尾指针
q=p->
e=q->
next=q->
if(*tail=q>
*tail=p。
//尾指针指向最后节点
free(q>
循环队列(CircularQueue>
:
存储队列的数组被当作首尾相接的表处理。
队头、队尾指针加1时从maxSize-1直接进到0,可用语言的取模(余数>
运算实现。
队头指针进1:
front=(front+1>
%maxSize。
队尾指针进1:
rear=(rear+1>
队列初始化:
front=rear=0。
队空条件:
front==rear。
队满条件:
(rear+1>
%maxSize==front
循环队列的进队和出队:
优先级队列:
是不同于先进先出队列的另一种队列。
每次从队列中取出的是具有最高优先权的元素
2.4串
字符串是非数值处理应用中重要的处理对象。
字符串是由某字符集上的字符所组成的任何有限字符序列。
当一个字符串不包含任何字符时,称它为空字符串。
一个字符串所包含的有效字符个数称为这个字符串的长度。
一个字符串中任一连续的子序列称为该字符串的子串。
包含子串的字符串相应称为主串。
通常称该字符在序列中的序号为该字符在串中的位置。
字符串的串值必须用单引号括,‘c1c2c3…cn起来,但单引号本身不属于串,它的作用是为了避免于变量名和数的常量混淆。
在C语言中,字符串常量是用一对双引号括住若干字符来表示,如“Iamastudent”
字符串通常存于字符数组中,每个字符串最后一个有效字符后跟一个字符串结束符“\0”.系统提供的库函数形成的字符串会自动加结束符号,而用户的应用程序中形成的字符串必须由程序自行负责添加字符串结束符号。
两个串相等当且仅当两个串的值相等,长度相等并且各个对应位置的字符都相等。
常用的字符串的基本操作有7种。
ASSING<
s,t)和CREAT<
s,ss)赋值操作
EQUAL<
s,t)判等函数
s)求长度函数
CONCAT<
s,t)联接函数
SUBSTR<
s,start,len)求子串函数
INDEX<
s,t)定位函数
REPLACE<
s,t,v)置换函数
s,pos,t)插入函数
s,pos,t)删除函数
(1>
求字符串长,intstrlen(chars>
2)串复制(copy>
char*strcpy(charto,charfrom>
该函数将串from复制到串to中,并且返回一个指向串to的开始处的指针。
(3>
联接(concatenation>
char
strcat(charto,charfrom>
该函数将串from复制到串to的末尾,并且返回一个指向串to的开始处的指针。
(4>
串比较<
compare>
intstrcmp(chars1,chars2>
该函数比较串s1和串s2的大小,当返回值小于0,等于0或大于0时分别表示s1<
s2或s1=s2或s1>
s2
例如:
result=strcmp(“baker”,”Baker”>
result>
result=strcmp(“12”,”12”>
result=0
result=strcmp(“Joe”,”Joseph”>
result<
5)字符定位(index>
charstrchr(chars,charc>
该函数是找c在字符串中第一次出现的位置,若找到则返回该位置,否则返回NULL。
字符串的静态存储:
顺序存储,用一组地址连续的地址单元存储串的字符序列,特别是在PASCAL程序语言中还可以采用紧缩数组来实现。
字符串的动态存储:
采用链表的方式存储字符串,节点的大小可以不同,即每个节点可以存放的字符数是不同的。
对于节点大小大于1的链表,需要设置头指针和尾指针来定位和串连接。
存储密度:
串值所站的存储位/实际分配的存储位
实际应用的串处理系统中采用的是动态存储结构,每个串的值各自存储在一组地址连续的存储单元中,存储地址在程序执行过程中动态分配。
利用串名和串值之间的的对应关系来建立存储映象来访问串。
2.5数组
数组是最常用的数据结构之一,在程序中,数组常用来实现顺序存储的线性表。
数组由固定个数的元素组成,全部元素的类型相同,元素依次顺序存储。
每个元素对应一个下标,数组元素按数组名和元素的下标引用,引用数组元素的下标个数称为数组的维数。
在C语言中,n个元素的数组中,第一个元素的下标为0,最后一个的下标为n-1。
数组可以分为一维、二维……N维数组,取决于引用数组元素的下标的个数;
一维数组:
二维数组和三维数组:
数组可以分为静态数组和动态数组两类,所谓静态就是指数组的空间存储分配是在使用之前还是在程序运行当中分配,静态数组就是在定义时必须进行空间分配,也就是固定数组的大小,这样就不利于数组的扩展。
同样动态数组就是在程序运行过程中进行数组的赋值或者是空间的分配,动态数组一般采用链表的存储结构,而静态数组一般采用顺序存储结构。
数组元素可以是任何类型的,当元素本身又是数组时,就构成多维数组。
多维数组是一维数组的推广,多维数组中最常用的是二维数组。
多维数组的所有元素并未排在一个线性序列里,要顺序存储多维数组按需要按一定次序把所有的数组元素排在一个线性序列里,常用的排列次序有行优先顺序和列优先顺序。
对于多维数组,C语言按行优先顺序存放。
对于数组,通常只有两种操作:
给定一个下标,存取相应的数据元素
给定一组下标,修改相应数据元素的某一个或几个数据项的值。
一般用多维数组表示矩阵,具体有以下几种类型:
对称矩阵:
A[i,j]==A[j,i]
三角矩阵:
以主对角线划分,三角矩阵有上三角和下三角两种。
上三角矩阵中,它的下三角<
不包括主对角线)中的元素均为常数。
下三角矩阵正好相反,它的主对角线上方均为常数,在大多数情况下,三角矩阵常数为零。
三角矩阵可压缩存储到向量sa[0..n(n+1>
/2]中,sa[k]和aij的对应关系是:
k=
3、对角矩阵
对角矩阵中,所有的非零元素集中在以主对角线为了中心的带状区域中,即除了主对角线和主对角线相邻两侧的若干条对角线上的元素之外,其余元素皆为零。
当∣i-j∣>
1时,元素a[i,j]=0。
LOC(i,j>
=LOC(0,0>
+[3*i-1+(j-i+1>
]=LOC(0,0>
+(2i+j>
4.稀疏矩阵
简单说,设矩阵A中有s个非零元素,若s远远小于矩阵元素的总数<
即s≦m×
n),并且分布没有一定规律。
用顺序存储结构的三元组对稀疏矩阵进行存储,分别记录行、列和值
十字链表:
由于非零元的位置和个数变化,所以用链表存储更恰当;
在这种情况下采用十字链表来表示,每个非零元用一个节点表示,节点中有行、列还有向下的域和线右的域;
此外还需要一个指向列链表的表头节点和指向行链表的表头节点。
还可以设置一个指向整个十字链表的表头节点。
还可以把列表头和行表头节点组成数组,便于操作;
各种广义表示意图
2.6树
2.6.1概述
树型结构是一类重要的非线性数据结构。
其中以树和二叉树最为常用,直观看来,树是以分支关系定义的层次结构。
树是由一个或多个结点组成的有限集T,它满足以下两个条件:
I.
有一个特定的结点,成为根结点
II.其余的结点分成m<
m>
=0)个互不相交的有限集T0,T1,…,Tm-1。
其中每个集合又都是一棵树,称T0,T1,…,Tm-1为根结点的子树。
这里可以看出树的定义是递归的,即一棵树由子树构成,子树又由更小的子树构成。
一个结点的子树数目,称为结点的度。
树中各结点的度的最大值则称为树的度。
树中结点的最大层次称为树的深度。
如果将树中结点的各子树看成从左到右是有次序的<
即不能交换),则称该树为有