1.1.1 关于数据组织
例一.如何在书架上摆放图书?
思考:图书的摆放钥匙的两个相关操作方便实现
操作1:新书怎么插入?
操作2:怎么找到某本指定的书?
方法1:随便放
操作1:哪里有空放哪里
操作2:累死
方法2:按照书名的拼音字母顺序摆放
操作1:逐个对比插入,后边的书向后挪位
操作2:二分查找
方法3:把书架划分成几块区域,每块区域指定摆放某种类型的图书,在每种类别内,按照书名的拼音字母顺序摆放
操作1:先定类别,二分查找确定位置,移出空位
操作2:先定类别,再二分查找
问题:空间如何分配?类别应该分多细?
解决问题方法的效率,跟数据的组织方式有关。
1.1.2 关于空间使用
例二.写程序实现一个函数PrintN,使得传入一个正整数为N的参数后,能顺序打印从1到N的全部正整数
法一:
void PrintN ( int N )
{
int i;
for(i=1;i<=N;i++)
{
printf("%d\n",i);
}
return;
}
循环实现
法二:
void PrintN ( int N )
{
if( N )
{
PrintN( N - 1 );
printf("%d\n",N);
}
return;
}
递归实现
问题:当输入100000时,循环可以打印,但是递归的函数无法打印
因为递归所占用的内存空间巨大,以至于计算机无法承受,从而终止了。
解决问题方法的效率,跟空间的利用效率有关。
1.1.3 关于算法效率
例三:写程序计算给定多项式在给定点x处的值
法一:低级算法(从外向内推算)
法二:秦九韶算法,普遍认可(从内向外推算)
秦九韶算法是一种将一元n次多项式转换为n个一次式的简化算法,其算法核心是提公因数。
问题:为什么法二算法比法一算法更好?
因为法二算法效率更高,所用时间更短。
解决问题方法的效率,跟算法的巧妙程度有关。
1.1.4 抽象数据类型
所以什么是数据结构?
- 数据对象在计算机中的组织方式
(1).逻辑结构
以例一中书架为例子,方法一一头一尾的书是顺序摆放的,每一本书前面只有一本书后面也只有一本书,如果给每一本书一个编号的话,每一个编号对应一本书,这种一对一的结构叫做线性结构;
另外一种组织方式是先把图书分类,那么一个类别对应很多本书,这是一个一对多的逻辑结构,称为树形结构。
假设我们还统计一本书有哪些人买过,买了这本书的人还买过其他什么书,也就是一本书对应很多人,一个人对应很多本书,这种多对多的关系称为图。
(2).物理存储结构
即各种逻辑结构在计算机当中应该怎样存储,是用数组还是链表。 - 数据对象必定与一系列加在其上的操作相关联
- 完成这些操作所用的方法就是算法
怎样描述数据结构?
抽象数据类型(Abstract Data Type)
- 数据类型
- 数据对象集
- 数据集合相关联的操作集
- 抽象:描述数据类型的方法不依赖于具体实现
- 与存放数据的机器无关
- 与数据存储的物理结构无关
- 与实现操作的算法和编程语言均无关
只描述数据结构对象集和相关操作集==“是什么”,并不涉及“如何做到”==的问题。
例四.“矩阵”的抽象数据类型定义
1.2.1 算法的定义
什么是算法?
定义:
算法(Algorithm)
- 一个有限指令集
- 接受一些输入(有些情况下不需要输入)
- 产生输出
- 一定在有限步骤之后终止
- 每一条指令必须
- 有充分明确的目标,不可以有歧义
- 计算机能处理的范围之内
- 描述应不依赖于任何一种计算机语言以及具体的实现手段
抽象 ——
List到底是数组还是链表(虽然看上去很像数组)?
Swap用函数还是用宏去实现?
1.2.2 什么是好的算法?
1.空间复杂度S(n) – 根据算法写成的程序在执行时占用存储单元的长度。这个长度往往与输入数据的规模有关,空间复杂度过高的算法可能导致使用的内存超限,造成程序非正常中断。
2.时间复杂度O(n)– 根据算法写成的程序在执行时耗费时间的长度。这个长度往往也与输入数据的规模有关。时间复杂度过高的低效算法可能导致我们在有生之年都等不到运行结果。一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数,用T(n)表示,若有某个辅助函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
在各种不同算法中,若算法中语句执行次数为一个常数,则时间复杂度为O(1),另外,在时间频度不相同时,时间复杂度有可能相同,如T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。 按数量级递增排列,常见的时间复杂度有:Ο(1)<Ο(logn)<Ο(n)<Ο(nlogn)<Ο(n^2^)<Ο(n^3^)<…<Ο(2^n^)<Ο(n!)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。
在调用函数之前,系统会保留当前程序运行占用的存储空间,于是递归函数就会出现上图所示的情况;而循环实现的程序只用了一个临时变量和一个for循环,没有涉及到任何程序调用的问题,所以无论N有多大,程序所占用的空间始终是固定的,不会随着N的