数据结构与排序.docx
《数据结构与排序.docx》由会员分享,可在线阅读,更多相关《数据结构与排序.docx(87页珍藏版)》请在冰豆网上搜索。
![数据结构与排序.docx](https://file1.bdocx.com/fileroot1/2023-4/21/eb204793-4f6e-4f50-b3c5-2cadad69e15b/eb204793-4f6e-4f50-b3c5-2cadad69e15b1.gif)
数据结构与排序
一.数据结构知识
数据结构是计算机软件的一门基础课程,计算机科学各个领域及有关的应用软件都要用到各种数据结构.语言编译要使用栈、散列表及语法树;操作系统中用队列、存储管理表及目录树等;数据库系统运用线性表、多链表及索引树等进行数据管理;而在人工智能领域,依求解问题性质的差异将涉及到各种不同的数据结构,如广义表、集合、搜索树及各种有向图等等。
学习数据结构目的是要熟悉一些最常用的数据结构,明确数据结构内在的逻辑关系,知道它们在计算机中的存储表示,并结合各种典型应用说明它们在进行各种操作时的动态性质及实际的执行算法,进一步提高软件计和编程水平。
通过对不同存储结构和相应算法的对比,增强我们根据求解问题的性质选择合理的数据结构,并将问题求解算法的空间、时间及复杂性控制在一定范围的能力。
软件设计师考试大纲对数据结构部分的要求是熟练掌握常用数据结构和常用算法,因此,本专题从数据结构的概述出发,对基本的概念引出常用的数据结构类型的介绍和讲解,同时在讲解各种数据结构中间采用算法与数据结构相结合的方式,在算法步骤中使用数据结构,对数据结构的重点、难点进行了分析,最后讲解了与数据结构紧密相关的排序和查找算法,以及一些以往考试题的分析。
1.数据结构概述
数据结构研究了计算机需要处理的数据对象和对象之间的关系;刻画了应用中涉及到的数据的逻辑组织;也描述了数据在计算机中如何存储、传送、转换。
学习数据结构注意的问题:
n系统掌握基本数据结构的特点及其不同实现。
n了解并掌握各种数据结构上主要操作的实现及其性能(时间、空间)的分析。
n掌握各种数据结构的使用特性,在算法设计中能够进行选择。
n掌握常用的递归、回溯、迭代、递推等方法的设计
n掌握自顶向下、逐步求精的程序设计方法。
n掌握自顶向下、逐步求精的程序设计方法。
在学习数据结构的知识之前,我们要了解一下数据结构中的基本概念。
数据:
对客观事物的符号表示,在计算机中就是指所有能输入到计算机中并被计算机程序所处理的符号的总称。
数据项:
是数据的不可分割的最小单位;
数据元素:
是数据的基本单位,在计算机程序中通常作为一个整体进行处理;一个数据元素可由若干个数据项组成。
数据对象:
是性质相同的数据元素的集合,是数据的一个子集。
数据结构上的基本操作:
◆插入操作
◆删除操作
◆更新操作
◆查找操作
◆排序操作
数据结构是指数据对象及相互关系和构造方法,一个数据结构B形式上可以用一个二元组表示为B=(A,R)。
其中,A是数据结构中的数据(称为结点)的非空有限集合,R是定义在A上的关系的非空有限集合。
根据数据元素之间的关系的不同特性,通常有下列4类基本结构。
Ø集合——结构中的数据元素除了“同属于一个集合”的关系外,别无其他关系。
Ø线性结构——结构中的数据元素之间存在一个对一个的关系。
Ø树形结构——结构中的元素之间存在一个对多个的关系。
Ø图状结构或网状结构——结构中的元素之间存在多个对多个的关系。
数据结构中,结点与结点间的相互关系是数据的逻辑结构。
数据结构在计算机中的表示(又称为映象)称为数据的物理结构,也称存储结构。
数据元素之间的关系在计算机中有两种不同的表示方式:
顺序映象和非顺序映象,并由此得到两种不同的存储结构:
顺序存储结构和链式存储结构。
任何一个算法的设计取决于选定的数据(逻辑)结构,而算法的实现依赖于采用的存储结构。
数据的逻辑结构分为两类:
线性结构:
线性表、栈、队列和串
非线性结构:
树、图
数据的存储方法有四类:
顺序存储方法
链接存储方法
索引存储方法
散列存储方法
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)执行线性表的插入和删除操作要移动其他元素,不够方便;
◆链式存储
线性表链接存储是用链表来存储线性表,。
单链表(线性链表):
从链表的第一个表元开始,将线性表的结点依次存储在链表的各表元中。
链表的每个表元除要存储线性表结点的信息以外,还要有一个成分来存储其后继结点的指针。
线性链表的特点是:
每个链表都有一个头指针,整个链表的存取必须从头指针开始,头指针指向第一个数据元素的位置,最后的节点指针为空。
当链表为空时,头指针为空值;链表非空时,头指针指向第一个节点。
链式存储的缺点:
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);
}
进栈:
voidpush(seqstack*s,datatypex)
{
if(stackfull(s))
error(“stackverflow”);
s->data[++s->top]=x;
}
判断栈空:
intstackempty(seqstack*s)
{
return(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;
q–>next=p–>top;
p–>top=q;
}
出栈:
Datatypepop(linkstack*p)
{
datatypex;
stacknode*q=p–>top;
if(stackempty(p)
error(“stackunderflow.”);
x=q–>data;
p–>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]=-1b[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;
p->next=*top;//指向栈顶指针
*top=p;
return0;
}
[函数②]
intpop(PNODE*top,int*e)
{
PNODEp=*top;
if(p==NULL)return–1;
*e=p->value;
*top=p->next;//栈顶指向取出的数的指针
free(p);
return0;
}
[函数③]
intenQueue(PNODE*tail,inte)
{PNODEp,t;
t=*tail;
p=(PNODE)malloc(sizeof(NODE));
if(!
p)return–l;
p—>value=e;
p—>next=t—>next;
t->next=p;//将元素加在尾指针后
*tail=p;
return0;
}
[函数④]
intdeQueue(PNODE*tail,inte)
{
PNODEp,q;
if((*tail)->next==*tail)return–1;//队列已经空
p=(*tail)->next;//p获得尾指针
q=p->next;
e=q->value;
p->next=q->next;
if(*tail=q)
*tail=p;//尾指针指向最后节点
free(q);
return0;
)
循环队列(CircularQueue):
存储队列的数组被当作首尾相接的表处理。
队头、队尾指针加1时从maxSize-1直接进到0,可用语言的取模(余数)运算实现。
队头指针进1:
front=(front+1)%maxSize;
队尾指针进1:
rear=(rear+1)%maxSize;
队列初始化:
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)判等函数
LENGTH(s)求长度函数
CONCAT(s,t)联接函数
SUBSTR(s,start,len)求子串函数
INDEX(s,t)定位函数
REPLACE(s,t,v)置换函数
INSERT(s,pos,t)插入函数
DELETE(s,pos,t)删除函数
(1)求字符串长,intstrlen(chars)
(2)串复制(copy)
char*strcpy(charto,charfrom);
该函数将串from复制到串to中,并且返回一个指向串to的开始处的指针。
(3)联接(concatenation)
charstrcat(charto,charfrom)
该函数将串from复制到串to的末尾,并且返回一个指向串to的开始处的指针。
(4)串比较(compare)
intstrcmp(chars1,chars2);
该函数比较串s1和串s2的大小,当返回值小于0,等于0或大于0时分别表示s1s2
例如:
result=strcmp(“baker”,”Baker”)result>0
result=strcmp(“12”,”12”);result=0
result=strcmp(“Joe”,”Joseph”);result<0
(5)字符定位(index)
charstrchr(chars,charc);
该函数是找c在字符串中第一次出现的位置,若找到则返回该位置,否则返回NULL。
字符串的静态存储:
顺序存储,用一组地址连续的地址单元存储串的字符序列,特别是在PASCAL程序语言中还可以采用紧缩数组来实现。
字符串的动态存储:
采用链表的方式存储字符串,节点的大小可以不同,即每个节点可以存放的字符数是不同的。
对于节点大小大于1的链表,需要设置头指针和尾指针来定位和串连接。
存储密度:
串值所站的存储位/实际分配的存储位
实际应用的串处理系统中采用的是动态存储结构,每个串的值各自存储在一组地址连续的存储单元中,存储地址在程序执行过程中动态分配。
利用串名和串值之间的的对应关系来建立存储映象来访问串。
2.5数组
数组是最常用的数据结构之一,在程序中,数组常用来实现顺序存储的线性表。
数组由固定个数的元素组成,全部元素的类型相同,元素依次顺序存储。
每个元素对应一个下标,数组元素按数组名和元素的下标引用,引用数组元素的下标个数称为数组的维数。
在C语言中,n个元素的数组中,第一个元素的下标为0,最后一个的下标为n-1;
数组可以分为一维、二维……N维数组,取决于引用数组元素的下标的个数;
一维数组:
二维数组和三维数组:
数组可以分为静态数组和动态数组两类,所谓静态就是指数组的空间存储分配是在使用之前还是在程序运行当中分配,静态数组就是在定义时必须进行空间分配,也就是固定数组的大小,这样就不利于数组的扩展。
同样动态数组就是在程序运行过程中进行数组的赋值或者是空间的分配,动态数组一般采用链表的存储结构,而静态数组一般采用顺序存储结构。
数组元素可以是任何类型的,当元素本身又是数组时,就构成多维数组。
多维数组是一维数组的推广,多维数组中最常用的是二维数组。
多维数组的所有元素并未排在一个线性序列里,要顺序存储多维数组按需要按一定次序把所有的数组元素排在一个线性序列里,常用的排列次序有行优先顺序和列优先顺序。
对于多维数组,C语言按行优先顺序存放。
对于数组,通常只有两种操作:
u给定一个下标,存取相应的数据元素
u给定一组下标,修改相应数据元素的某一个或几个数据项的值。
一般用多维数组表示矩阵,具体有以下几种类型:
对称矩阵:
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