算法与数据结构在知识体系中的位置是非常重要的。
算法与数据结构知识体系
概括地说:
算法与数据结构是一门讨论“描述现实世界实体的数学模型(非数值计算)及其上的操作在计算机中如何表示和实现”的学科。
——唐·欧·克努特(美国)
1、数据:
所有能被输入到计算机中,且能被计算机处理的符号(数值、字符等)的集合。是计算机操作的对象的总称。是计算机处理的信息的某种特定的符号表示形式。
2、数据元素(结点):
是数据(集合)中的一个“个 体”,在计算机中通常作为一个整体进行考虑和处理。是数据结构中讨论的基本单位。
如:整数“5”,字符“N”等。(是不可分割的“原子”)
数据元素也可以由若干款项构成。
例如:描述一个学生的数据元素,其中每个款项称为一个“数据项”,它是数据结构中讨论的最小单位
姓 名 | 学号 | 班号 | 性别 | 出生日期 | 入学成绩 |
---|---|---|---|---|---|
年 月 日 |
其中第一排是原子项,第二排的“年 月 日”是组合项,组合成出生日期这个原子项。
3、结点(数据元素)的类型
(1)基本数据类型
(2)复合数据类型
4、数据结构:
有一个特性相同的数据元素的集合,如果在数据元素之间存在一种或多种特定的关系,则称为一个数据结构。
带结构的数据元素的集合——指的是数据元素之间存在的关系
例如:
3214, 6587, 9345 ─ a1(3214), a2(6587), a3(9345)
则在数据元素 a1、a2 和 a3 之间存在着“次序”关系
3214 | 6587 | 9345 |
---|---|---|
a1 | a2 | a3 |
6587 | 3214 | 9345 |
---|---|---|
a2 | a1 | a3 |
又例,在2行3列的二维数组中六个元素{a1, a2, a3, a4, a5, a6}
之间存在两个关系:
a1 | a2 | a3 |
---|---|---|
a3 | a4 | a5 |
行的次序关系:
row = {
列的次序关系:
col = {
若在 6 个数据元素{a1, a2, a3, a4, a5, a6} 之间存在如下的次序关系:{
可见,不同的“关系”构成不同的“结构”。
数据结构是相互之间存在着某种逻辑关系的数据元素的集合。
5、从关系或结构分类
数据结构可归结:
为以下四类:
数据结构包括“逻辑结构” 和“物理结构”两个方面(层次):
逻辑结构:
是对数据元素之间的逻辑关系的描述,它可以用一个数据元素的集合和定义在此集合上的若干关系来表示;
物理结构:
是逻辑结构在计算机中的表示和实现,故又称“存储结构” 。
数据结构的形式定义描述为:
数据结构是一个二元组
Data_Structures = (K, R)
例如:定义 “班集体”为一个数据结构
Class = (K, R)
K = { a, b1,…,bn, c1,…cn, d1,…dn }
R = { R1, R2 }
R1 = { ,,}
R2 = {
其中:K 是数据结点的有限集, R 是 K上关系的有限集。
——逻辑结构在存储器中的映象
数据元素(结点)的映象方法:
用二进制位(bit)的位串表示数据元素
(321)10 = (501)8 = (101000001)2
A = (101)8 = (001000001)2
关系的映象方法:(表示
1、顺序的方法
以相对的存储位置表示后继关系
例如:令 y 的存储位置和 x 的存储位置之间差一个常量 C ,而 C 是一个隐含值,整个存储结构中只含数据元素本身的信息
顺序存储结构常用于线性数据结构,将逻 辑上相邻的数据元素存储在物理上相邻的存储单元里。
顺序存储结构的三个弱点:
2、链接的方法
以附加信息(指针)表示后继关系需要用一个和 x 在一起的附加信息指示 y 的存储位置。
每个节点都由两部分组成:数据域和指针域。
数据域存放元素本身的数据,
指针域存放指针。
数据元素之间逻辑上的联系由指针来体现。
链接存储结构特点:
3、索引的方法
索引法是顺序存储法的一种推广,它也使用整数编码来访问数据结点位置。
索引方法是要建造一个由整数域Z映射到存储地址域D的函数Y:Z–>D,把结点的整数索引值z∈Z映射到结点的存储地址d∈D。它称为索引函数,一般而言它并不象数组那样,是简单的线性函数。当数据结点长度不等的情况下,索引函数就无法用线性表达式给出。
4、散列的方法
散列方法是索引方法的一一种延伸和扩展。利用一种称为散列函数(hash functions)进行索引值的计算,然后通过索引表求出结点的指针地址。
散列函数是将字符串s映射到非负整数z的一类函数h: S–> Z,对任意的s∈S,散列函数h(s)=z, z∈Z。
数据的逻辑结构与存储结构密切相关
(Abstract Data Type 简称ADT)
1、定义:是指一个数学模型以及定义在此数学模型上的一组操作。
2、特点:将数据和操作封装在一起。
3、目的 :隐藏运算实现的细节和内部数据结构;提高复用的力度和粒度。
1、数据抽象:
用ADT描述程序处理的实体时,强调的是其本质的特征、其所能完成的功能以及它和外部用户的接口(即外界使用它的方法)。
2、数据封装:
将实体的外部特性和其内部实现细节分离,并且对外部用户隐藏其内部实现细节。(使用与实现相分离)
抽象数据类型可用(D,S,P)三元组表示
其中,
ADT 抽象数据类型名 {
数据对象:〈数据对象的定义〉
数据关系:〈数据关系的定义〉
基本操作:〈基本操作的定义〉
} ADT 抽象数据类型名
抽象数据类型
template <class Type> // 模板参数为类型Type
class className {
private: // 数据结构的的取值类型和取值空间
Type dataList; // 定义数据及其存储方式
……
public: // 运算集
methodName(); // 定义对数据的操作
……
};
算法是为了求解问题而给出的指令序列。
1、算法的五个重要特性:
2、算法分类
1、计算复杂性理论(computational complexity theory)指出,理论上存在一大类难解问题,它们虽然存在着求解算法,但是在算法的计算时间上,都是组合爆炸型的求解算法。
2、组合爆炸型是指随着问题的规模n的增大,算法的时间开销不能约束在n的k阶多项式数量范围内。
3、比较常见的难解问题有:图论中的求最优巡游路径问题,判定命题逻辑公式是否为恒真等。
1、解决同一个问题存在多种算法,评估各算法的好坏或据此设计出更好的算法的依据是:运行该算法所需要的计算机资源的多寡,所需越大复杂性越高。
最重要的资源:时间(处理器)和空间(存储器)
2、评价一个算法优劣的重要依据是看执行该算法的程序需要占用多少机器资源,即程序所用算法运行时所要花费的时间代价和程序中使用的数据结构占有的空间代价。
3、算法时间复杂性
不能用诸如微秒、纳秒这样的真实时间单位。
例如:
算法的渐近分析
(1)算法的渐进分析(大O表示法)就是要估计,当数据规模n逐步增大时,资源开销T(n)的增长趋势。即得到一个大 O渐进表达式,简写为:
r a t e n → + ∞ T ( n ) = O ( F ( n ) ) rate_{n\rightarrow+\infty} T(n) = O(F(n)) raten→+∞T(n)=O(F(n))
严格的数学定义:
这时称T(n)的时间复杂度为f(n)数量级。(或称为:算法具有O(f(n))的计算时间)
(2)大O表示法的运算规则
例1:
1. x=0; //1 T(n)=1
2. y=0;//1 T(n)=1
3. for (k=1;k<=n;k++) //n
x++; //n
//T(n)=1+2n 该算法时间复杂度为: T(n)=O(n)
4. for (i=1;i<=n;i++) //n
for (j=1;j<=n;j++) //n*n
y++; //n*n
//T(n)=2n+2n2+1
//当n充分大时, T(n)与n2是同阶的。
//该算法时间复杂度为:T(n)=O(n2)
例2:
for (i=1; i<=n; ++i) // n
for (j=1; j<=n; ++j) // n*n
c [i][j]=0; // n2
//T(n)=2n2+2n+1
定理:若A(n)=a m nm +a m-1 nm-1 +…+a1n+a0是一个m次多项式,则A(n)=O(nm)
例3:
{++x;s=0;}
将x自增看成是基本操作,则语句频度为1,即时间复杂度为O(1)
如果将s=0也看成是基本操作,则语句频度为2,其时间复杂度仍为O(1),即常量阶。
例4:
for(I=1;I<=n;++I)
{++x;s+=x;}
循环体中语句频度为:2n, 其时间复杂度为:O(n),即时间复杂度为线性阶。
例5:
for(I=1;I<=n;++I)
for(j=1;j<=n;++j)
{++x;s+=x;}
循环体中语句频度为: 2 n 2 2n^2 2n2其时间复杂度为:O($n^2S) ,即时间复杂度为平方阶。
例6:
求两个方阵的乘积 C=A*B
#define n 自然数
MATRIXMLT(float A[n][n],float B[n][n],float C[n][n])
{
int i,j,k;
for(i=0;i<n;i++) //n
for(j=0;j<n;j++) //n*n
{
C[i][j]=0; //n*n
for( k=0;k<n;k++) //n*n*n
C[i][j]=A[i][k]*B[k][j] //n*n*n
}
}
分析:由于是一个三重循环,每个循环从1到n,则总次数为: 2 n 3 + 3 n 2 + 2 n + 1 2n^3+3n^2+2n+1 2n3+3n2+2n+1。所以,时间复杂度为T(n)=O( n 3 n^3 n3),即时间复杂度为立方阶。
例7:
若只对每个子数组的前5个元素求和,则相应的代码可采用下面的方式:
for ( i=4; i<n; i++)
for (j = i-3, sum = a[i-4]; j <= i; j++)
sum += a[j];
(3)用于渐进分析的常见的F(n)
以下六种计算算法时间的多项式是最常用的。
其关系为:
O(1)
指数时间的关系为:
O( 2 n 2^n 2n)
当n取得很大时,指数时间算法和多项式时间算法在所需时间上非常悬殊。因此,只要有人能将现有指数时间算法中的任何一个算法化简为多项式时间算法,那就取得了一个伟大的成就。
(4)大O表式法的性质
若符号a是不依赖于n的任意常数
1、最坏、最好和平均情况
例一:
求一个数组的所有有序子数组中最长的一个:
for (i = 0, length=1; i<n-1; i++) {
for (j1 = j2 = k = i; k < n-1 && a[k] < a[k+1]; k++,j2++);
if (length < j2 - j1 +1)
length = j2 - j1 + 2;
}
譬如,在数组[1, 8, 1, 2, 5, 0, 11, 9]中,这个最长的有序子数组为[1,2,5],长度为3。时间代价和数组 a 中元素的实际取值状态很相关。
示例:最好
数组a的所有元素是以降序方式输入
min{ complexity(size(y)) | y ϵ \epsilon ϵInput}
示例:最坏
max {complexity(size(y)) | y ϵ \epsilon ϵInput }
示例:平均
2、最好、最坏、平均情况
1、空间开销的概念
2、时空资源的折中原理
1、仔细分析所要解决的问题,特别是求解问题所涉及的数据类型和数据间逻辑关系。
2、数据结构的初步设计往往在算法设计之先。
3、注意数据结构的可扩展性。包括考虑当输入数据的规模发生改变时,数据结构是否能够适应。同时,数据结构应该适应求解问题的演变和扩展。
4、数据结构的设计和选择也要比较算法的时空开销的优劣。