什么是数据结构
先了解一张图
计算中的各种数据,图片,视频…等等都放在内存中进行管理,排列成一列进行存放,在每列中又有许多小空间分别存放数据。
当数据存放在计算机内存时,决定了数据顺序和位置结构的就是数据结构
基本概念和术语
- 数据(data)
数据是对客观事物的符号表示,在计算机科学中是指所有能输入到计算机中并被计算机程序处理的符号的总称。 - 数据元素(data element)
数据元素是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理。 - 数据对象(data object)
数据对象是性质相同的数据元素的集合,是数据的一个子集。 - 数据结构(data structure)
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 - 结构(structure)
数据元素相互之间的关系称为结构。 - 数据元素的四种基本结构:集合、线性结构、树形结构、图状结构(网状结构)。
- 集合
结构中的数据元素之间除了“同属于一个集合”的关系外,别无其他关系。 - 线性结构
结构中的数据元素之间存在一个对一个的关系。 - 树形结构
结构中的数据元素之间存在一个对多个的关系。 - 图状结构(网状结构)
结构中的数据元素之间存在多个对多个的关系。
数据结构的形式定义为:数据结构是一个二元组Data_Structure = (D,S)
其中:D是数据元素的有限集,S是D上关系的有限集。
- 数据的逻辑结构
结构定义中数据元素之间关系的描述就是数据元素之间的逻辑关系。即数据的逻辑结构。 - 数据的物理结构(存储结构)
数据结构在计算机中的表示称为数据的物理结构(存储结构)。它包括数据元素的表示和关系的表示。
一个算法的设计取决于选定的数据(逻辑)结构,而算法的实现依赖于采用的存储结构
数据元素之间的关系在计算机中有两种不同的表示方法:顺序映像和非顺序映像,因此也得到两种不同的存储结构:顺序存储结构和链式存储结构。
- 顺序映像
特点:借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系 - 非顺序映像
特点:借助指示元素存储地址的指针表示数据元素之间的逻辑关系。 - 数据类型是一个值的集合和定义在这个值集上的一只操作的总称。
数据类型可以分为两类:非结构的原子类、结构类型
- 非结构的原子类
原子类型的值不可分解,如指针类型、空类型等等。 - 结构类型
结构类型的值由若干成分按某种结构组成,可以分解。 - 抽象数据类型(Abstract Data Type,简称ADT)
指一个数学模型以及定义在该模型上的一组操作。抽象数据类型的定义仅取决于它的一组逻辑特性,而与其在计算机内部如何表示和实现无关,即不论其内部结构如何变化,只要它的数学特性不变,都不影响其外部的使用。
抽象数据类型的定义由一个值域和定义在该值域上的一组操作组成,按照其值的不同特性,可分为如下三种类型:原子类型、固定聚合类型、可变聚合类型
- 原子类型
属原子类型的变量的值不可分割。这类抽象数据类型较少 - 固定聚合类型
属于该类型的变量的值由确定数目的成分按某种结构组成。 - 可变聚合类型
构成可变聚合类型“值”的成分的数目不确定。
采用如下的格式定义抽象数据类型:
ADT抽象数据类型名{
数据对象:<数据对象的定义>
数据关系:<数据关系的定义>
基本操作:<基本操作的定义>
}ADT抽象数据类型名
抽象数据类型的表示与实现
抽象数据类型Triplet的表示和实现:
//采用动态分配的顺序存储结构
typedef ElemType *Triplet
//----------基本操作的函数原型说明----------
//操作结果:构造了三元组T,元素e1,e2,e3分别被赋予参数v1,v2,v3的值
Status InitTriplet(Triplet &T,ElemType v1,ElemType v2,ElemType v3);
//操作结果:三元组T被销毁
Status DestroyTriplet(Triplet &T);
//初始条件:三元组T已存在,1<=i<=3
//操作结果:用e返回T的第I元的值
Status Get(Triplet T,int i,ElemType &e);
//初始条件:三元组T已存在,1<=i<=3
//操作结果:改变T的第i元值为e
Status Put(Triplet T,int i,ElemType &e);
//初始条件:三元组T已存在
//操作结果:如果T的3个元素按升序排列,则返回1,否则返回0
Status IsAscending(Triplet T);
//初始条件:三元组T已存在
//操作结果:如果T的3个元素按降序排列,则返回1,否则返回0
Status IsDescending(Triplet T);
//初始条件:三元组T已存在
//操作结果:用e返回T的3个元素中的最大值
Status Max(Triplet T,ElemType &e);
//初始条件:三元组T已存在
//操作结果:用e返回T的3个元素中的最小值
Status Min(Triplet T,ElemType &e);
//----------基本操作的实现----------
Status InitTriplet(Triplet &T,ElemType v1,ElemType v2,ElemType v3)
{
T = (ElemType *)malloc(3 * sizeof(ElemType));
if(!T) exit(OVERFLOW);
T[0] = v1;
T[1] = v2;
T[2] = v3;
return OK;
}
Status DestroyTriplet(Triplet &T)
{
free(T);
T = NULL;
return OK;
}
Status Get(Triplet T,int i,ElemType &e)
{
if(i < 1 || i > 3) return ERROR;
e = T[i - 1];
return OK;
}
Status Put(Triplet T,int i,ElemType &e)
{
if(i < 1 || i > 3) return ERROR;
T[i - 1] = e;
return OK;
}
Status IsAscending(Triplet T)
{
return(T[0] <= T[1])&&(T[1] <= T[2]);
}
Status IsDescending(Triplet T)
{
return(T[0] >= T[1])&&(T[1] >= T[2]);
}
Status Max(Triplet T,ElemType &e)
{
e = (T[0] >= T[1])?((T[0] >= T[2])?T[0]:T[2]):((T[1] >= T[2])?T[1]:T[2]);
return OK;
}
Status Min(Triplet T,ElemType &e)
{
e = (T[0] <= T[1])?((T[0] <= T[2])?T[0]:T[2]):((T[1] <= T[2])?T[1]:T[2]);
return OK;
}
算法和算法分析
1.算法
算法(algorithm)是对特定问题求解步骤的一种描述,它是指令的有限序列,其中每一条指令表示一个或多个操作。
- 算法的特性
- 有穷性
- 一个算法必须总是在执行有穷步之后结束,且每一步都可以在有穷时间内完成
- 确定性
- 算法中的每一条指令必须有确切的含义,读者理解时不会理解成另外的情况
- 可行性
- 算法中描述的操作应该可以通过已经实现的基本运算执行有限次来实现
- 输入
- 一个算法有零个或多个的输入,这些输入取自于某个特定的对象集合
- 输出
- 一个算法有零个或多个的输出,这些输出和输入有着某些特定关系的量
- 有穷性
- 算法设计的要求
设计“好”的算法应考虑以下的目标
- 正确性
- 设计或选择的算法应正能正确的反映需求
- 可读性
- 可读性好有助于人们对算法的理解,晦涩难懂的程序易于隐藏较多错误,难以调试和修改
- 健壮性
- 输入数据非法时,算法能够适当的作出反应或进行处理
- 效率与低存储量需求
- 效率指算法执行的时间、存储量需求指算法执行过程中所需要的最大存储空间
- 算法效率的度量
一个算法是由控制结构(顺序、分支和循环3种)和原操作(指固定数据类型的操作)构成的,算法时间复杂取决于两者的总和效果。
举例:下面所示代码为两个N*N矩阵相乘算法,该算法的执行时间与该基本操作重复执行的次数n^3成正比,记作T(n)=O(n ^3)。
for(i = 1; i<=n; ++i)
for(j = 1; j<=n; ++j)
{
c[i][j] = 0;
for(k = 1;k <= n; ++k)
c[i][j] += a[i][k] * b[k][j];
}
一般情况下,算法中的基本操作重复执行的次数是问题规模n的某个函数f(n),算法的时间度量记作:
T(n) = O(f(n))
它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度(asymptotic time complexity)
- 算法的存储空间需求
类似于算法的时间复杂度,空间复杂度是算法所需存储空间的量度,记作:
S(n) = O(f(n))
其中n为问题的规模。一个上机执行的程序除了需要存储空间来寄存本身所用指令、常数、变量和输入数据外,也需要一些对数据进行操作的工作单元和存储一些为实现计算所需信息的辅助空间。