1.2 基本概念和术语
数据结构包括两个层次:逻辑结构 和 存储结构
数据的逻辑结构 分为:线性结构(线性表包含在这里) 和 非线性结构
数据的存储结构 分为:顺序存储结构 和 链式存储结构
相同的逻辑结构,可以对应不同的存储结构。
数据结构 和 数据类型 的区别和联系:
(1)区别:
数据结构(Data Structure)是:相互之间存在一种或多种特定关系的数据元素的集合。
数据类型(Data Type)是:用来约束数据的解释。描述了数值的表示法、解释和结构,并以算法操作,或是对象在存储器中的存储区,或者其它存储设备。
(2)联系:
数据类型是高级程序设计语言中的一个基本概念,前面提到过顺序存储结构可以借助程序设计语言的数组类型描述,链式存储结构可以借助指针类型描述,所以数据类型和数据结构的概念密切相关。
抽象数据类型(Abstract Data Type,ADT)一般指由用户定义的、表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称。具体包括三部分:
(1)数据对象;
(2)数据对象上关系的集合;
(3)对数据对象的基本操作的集合。
抽象数据类型是从数据类型中衍生出来的定义,而非数据结构。
针对每种抽象数据类型定义中的数据对象,其数据存储结构要借助某一数据类型来描述。
(这里共存在三个变化的量。)
【第二个变化的量:数据存储结构,可能是顺序存储结构,也可能是 链式存储结构。
第三个变化的量:某一数据类型,可能是基本数据类型去描述复杂的抽象数据类型,也可能是用某一复杂的抽象数据类型去描述更复杂的抽象数据类型。
如用二叉链表、十字链表、邻接表、邻接多重表去描述树和图等非线性结构。】
数据逻辑结构的作用在于:
根据数据之间的逻辑结构关系,不断进行更高一级的数据抽象,构造出来了一系列基本数据类型、抽象数据类型。
P8 抽象就是抽取出实际问题的本质。
在计算机中使用二进制数来表示数据,在汇编语言中则可给出各种数据的十进制表示,十进制数据即是二进制数据的抽象。
在高级语言中,则给出十进制数据更高一级的数据抽象,出现了数据类型,如整型、实型、字符型等数据类型。
进一步利用这些基本的数据类型,构造出了线性表、栈、队列、树、图等复杂的抽象数据类型。
1.3 抽象数据类型的表示与实现
/* (1)抽象数据类型的定义部分 P9 */
ADT 抽象数据类型名{
数据对象: <数据对象的定义> /* 采用数学符号和自然语言描述 */
数据关系:<数据关系的定义> /* 采用数学符号和自然语言描述 */
基本操作:
基本操作名(参数表)//有两种参数:赋值参数只为操作提供输入值;引用参数以“& ”打头,除可提供输入值外,还将返回操作结果。
初始条件:<初始条件描述>
操作结果:<操作结果描述>
}ADT 抽象数据类型名
/* (2)预定义常量及类型: */
//函数结果状态代码
#define OK 1
#define ERROR 0
#define OVERFLOW -2
//Status是函数返回值类型,其值是函数结果状态代码。
typedef int Status;
/* (3)抽象数据类型的存储结构表示部分 P10
或者说:抽象数据类型定义中数据对象的 数据存储结构(不管是顺序存储结构,还是链式存储结构) 要借助 某一数据类型 来表示时的表示方式 */
数据存储结构用类型定义(typedef)描述;
数据元素类型约定为ElemType,由用户在使用该数据类型时自行定义。
/* (4)抽象数据类型的相应操作的具体实现部分 P10
(抽象数据类型的定义中,基本操作的算法都用如下格式的函数来描述)*/
函数类型函数名(函数参数表)
{
//算法说明
语句序列
}//函数名
/* (5)内存的动态分配与释放 */
使用new和delete动态分配和释放内存空间
分配空间 指针变量=new 数据类型:
释放空间 delete 指针变量;
/* 以复数为例,给出一个完整的抽象数据类型的定义、表示和实现 */
//(1)定义部分
ADT Complex{
数据对象:D = {e1,e2 | e1,e2 ∈R,R是实数集}
数据关系:S = {<e1,e2> | e1是复数的实部,e2是复数的虚部}
基本操作:
Creat(&C,x,y)
操作结果:构造复数C,其实部和虚部分别被赋以参数x和y的值。
GetReal(C)
初始条件:复数C已存在。
操作结果:返回复数C的实部值。
GetImag(C)
初始条件:复数C已存在。
操作结果:返回复数C的虚部值。
Add(C1,C2)
初始条件:C1,C2是复数。
操作结果:返回两个复数C1和C2的和。
Sub(C1,C2)
初始条件:C1,C2是复数。
操作结果:返回两个复数C1和C2的差。
}ADT Complex
//(2)存储表示部分
typedef struct //复数类型
{
float Realpart; //实部
float Imagepart; //虚部
} Complex;
//(3)相应操作的具体实现部分
void Create(&Complex C, float x, float y)
{ //构造一个复数
C.Realpart = x;
C.Imagepart = y;
}
float GetReal(Complex C)
{ //取复数C=x+yi的实部
return C.Realpart;
}
float GetImag(Complex C)
{ //取复数C=x+yi的虚部
return C.Imagepart;
}
Complex Add(Complex C1, Complex C2)
{ //求两个复数C1和C2的和sum
Complex sum;
sum.Realpart = C1.Realpart + C2.Realpart;
sum.Imagepart = C1.Imagepart + C2.Imagepart;
return sum;
}
Complex Sub(Complex C1, Complex C2)
{ //求两个复数C1和C2的差difference
Complex difference;
difference.Realpart = C1.Realpart - C2.Realpart;
difference.Imagepart = C1.Imagepart - C2.Imagepart;
return difference;
}
习题2
试举一个数据结构的例子,叙述其逻辑结构和存储结构两方面的含义和相互关系。
答案:
逻辑结构:
两个要素:一是数据元素;二是关系。
四类基本的数据逻辑结构:集合结构、线性结构、树结构、图/网结构
存储结构/物理结构:
两种基本的数据存储/物理结构:顺序存储结构(借助数组类型)、链式存储结构(借助指针类型)
逻辑结构与存储结构的相互关系:
①顺序存储结构是借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系。
因此顺序存储结构要求所有的元素依次存放在一片连续的存储空间中。
②链式存储结构中,与逻辑结构的关系是:
链式存储结构为了表示结点(即数据元素)之间的关系,需要给每个节点附加指针字段,用于存放后继元素的存储地址。
因此链式存储结构无需占用一整块存储空间。
标准答案:
例如有一张学生基本信息表,包括学生的学号、姓名、性别、籍贯、专业等。每个学生基本信息记录对应一个数据元素,学生记录按顺序号排列,形成了学生基本信息记录的线性序列。
对于整个表来说,只有一个开始结点(它的前面无记录)和一个终端结点(它的后面无记录),其他的结点则各有一个也只有一个直接前趋和直接后继。学生记录之间的这种关系就确定了学生表的逻辑结构,即线性结构。
这些学生记录在计算机中的存储表示就是存储结构。
如果用连续的存储单元(如用数组表示)来存放这些记录,则称为顺序存储结构;如果存储单元不连续,而是随机存放各个记录,然后用指针进行链接,则称为链式存储结构。
即相同的逻辑结构,可以对应不同的存储结构。
习题6
试分析下面各程序段的时间复杂度。
(1)x=90; y=100;
while(y>0)
if(x>100)
{x=x-10;y--;}
else x++;
答案:O(1)
解释:程序的执行次数为常数阶。P15
(2)for (i=0; i<n; i++)
for (j=0; j<m; j++)
a[i][j]=0;
答案:O(m*n)
解释:语句a[i][j]=0;的执行次数为m*n.
(3)s=0;
for i=0; i<n; i++)
for(j=0; j<n; j++)
s+=B[i][j];
sum=s;
答案:O(n*n)
解释:语句s+=B[i][j];的执行次数为n*n.
(4)i=1;
while(i<=n)
i=i*3;
答案:O(log₃n)
解释:语句i=i*3;的执行次数为 log₃n.P16
(5)x=0;
for(i=1; i<n; i++)
for (j=1; j<=n-i; j++)
x++;
答案:O(n*n)
解释:语句x++;的执行次数为n-1+n-2+……+1= n(n-1)/2.
(6)x=n; //n>1
y=0;
while(x≥(y+1)* (y+1))
y++;
答案:O(√n)
解释:语句y++;的执行次数为√n. (一元二次方程)