数据结构1
第一章
1.程序设计(Programming)是给出解决特定问题程序的过程,是软件构造活动中的重要组成部分。程序设计往往以某种程序设计语言为工具,给出这种语言下的程序。程序设计过程应当包括分析、设计、编码、测试、排错等不同阶段。专业的程序设计人员常被称为程序员。
2.程序设计=数据结构+算法
Programming = Data Structures + Algorithm
3.程序设计:为计算机处理问题编制一组指令集。
算法:处理问题的策略。
数据结构:问题的数学模型。
4.概括地说,数据结构描述现实世界实体的数学模型(非数值计算)及其上的操作在计算机中的表示和实现。
5.数据:所有能被输入到计算机中,并且能被计算机处理的符号的集合、计算机操作的对象的总称,是计算机处理的信息的某种特定的符号表示形式。
6.数据结构中的基本单位是数据元素,数据元素可以是数据项的集合,数据项是数据结构中的最小单位。
7.数据结构:带结构的数据元素的集合。
8.数据的逻辑结构可归纳为四类:线性结构、树形结构、图状结构、集合结构。集合中的数据元素不存在任何关系,但从某种意义上也是一种关系。
9.数据结构的形式定义为:数据结构是一个二元组Data_Structures=(D,S),其中:D是数据元素的有限集,S是D上关系的有限集。
10.数据的存储结构:逻辑结构在存储器中的印象。
11.关系的映象方法:(表示<x,y>的方法)
顺序映象:以存储位置的相邻表示后继关系。即y的存储位置和x的存储位置之间差一个常量C。而C是一个隐含值,整个存储结构中只含有数据元素本身的信息。
链式映象:以附加信息(指针)表示后继关系。x和y的存储位置没有固定的关系,需要用一个和x在一起的附加信息指示y的存储位置。
12.数据结构是一个值的集合和定义在此集合上的一组操作的总称。
13.抽象数据类型Abstract Data Type,简称ADT,是指一个数学模型以及定义在此数学模型上的一组操作。
14.ADT有两个重要的特征:(1)数据抽象,(2)数据封装。
数据抽象:用ADT描述程序处理的实体时,强调的是其本质的特征、其所能完成的功能以及它和外部用户的接口(即外界使用它的方法)。
数据封装:将实体的外部特性和其内部实现细节分离,并且对外部用户隐藏其内部实现细节。
15.抽象数据类型可以用(D,S,P)三元组表示,其中D是数据对象,S是D上的关系集,P是对D的基本操作集。数据结构和它的操作在实质上是一个整体。
16.ADT 抽象数据类型名
{
数据对象:<数据对象的定义>
数据关系:<数据关系的定义>
基本操作:<基本操作的定义>
}ADT 抽象数据类型名
17.抽象数据类型需要通过固有数据类型(基本数据类型)来实现。
18.算法是对问题求解的一种描述。算法是为了解决某类问题而规定的一个有限长的操作序列。一个算法必须满足以下五个重要特性:有穷性、确定性、可行性、有输入、有输出。
有穷性:对于任意一组合法输入值,在执行有穷步骤之后一定能结束,即:算法中的每个步骤都能在有限时间内完成。描述算法指令的条数是有限的。算法中的每一步的执行时间是有限的,这种有限不是数学上的有限,而是应该是合理的,如一个算法不应该执行几天甚至更长时间。
确定性:对于每种情况下所应执行的操作,在算法中都有确切的规定,使算法的执行者或阅读者都能明确其含义以及如何执行。并且在任何条件下,算法都只有一条执行路径。
可行性:算法中的所有操作都必须足够基本,都可以通过已经实现的基本操作运算有限次实现之。
有输入:作为算法加工对象的量值,通常体现为算法中的一组变量。有的算法表面上可以没有输入,实际上已被嵌入算法之中。
有输出:没有输出的算法是没有意义的。
19.设计算法时,通常应考虑达到以下目标:正确性、可读性、健壮性、高效率与低存储量需求。
20.设计的算法对于精心选择的、典型的、苛刻的几组输入数据能够得出满足要求的结果。
21.健壮性(鲁棒性):当输入的数据非法时,算法应当恰当地作出反映或进行相应处理,而不是产生莫名其妙的输出结果。并且处理出错的方法不应该是中断程序的执行,而应是返回一个表示错误或错误性质的值,以便在更高的抽象层次上进行处理。
22.通常有两种衡量算法效率的方法:(1)事后统计法,(2)事前分析估算法。
事后统计法就是将算法写成程序,在计算机上执行,缺点:必须执行程序,其它因素掩盖算法本质。
23.采用事前分析估算法,首先需要考虑和算法执行时间相关的因素:
(1)算法选用的策略
(2)问题的规模
(3)编写程序的语言
(4)编译程序产生的机器代码的质量
(5)计算机执行指令的速度
其中前面两条和设计算法相关的,后面三条和设计算法是无关的。
24.随着问题规模n的增长,算法执行时间的增长率和f(n)的增长率相同,则可记作:
T(n)=O(f(n))
称为T(n)为算法的[渐进]时间复杂度。
25.算法=控制结构+若干原操作
原操作本来是计算机执行的一条指令,但在高级语言中,无法表示,因此在高级语言中将固有数据类型的操作称为原操作。
算法的执行时间=∑(原操作i的执行次数*原操作i的执行时间)
由于原操作的执行时间是一个定值,因此在估计算法执行时间时往往忽略原操作的执行时间,那么算法的执行时间与原操作执行次数之和成正比。
在一个算法中往往有许多的原操作,但只有部分原操作执行次数占的比例很大,因此从算法中选取一种对于所研究的问题来说是基本操作的原操作,以该基本操作在算法中重复执行的次数作为算法运行时间的衡量准则。
例如:
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
for(k=1;k<=n;k++)
{
array[i][j][k] = 0;
}
}
}
基本操作是赋值操作。时间复杂度是O(n*n*n)。即赋值操作要进行n*n*n次。
时间频度:一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。
26.算法的空间复杂度S(n)=O(g(n))。
算法的存储量包括:输入数据所占空间、程序本身所占空间、辅助变量所占空间。
对于一个问题的不同算法,输入数据一样的,程序本身所占的空间也差不多,因此算法的空间复杂度只与辅助变量所占空间有关。
若所需额外空间相对于输入数据量来说是常数,则称此算法为原地工作,算法的空间复杂度为O(1)。
例如:
void Bubble_Sort(int *sort_array,int n)
{
int temp;
int i,j;
for(i=1;i<n;i++)//最多做n-1趟
{
for(j=n-1;j>=i;j--)//每一趟排完一个较小的数,较小的数一点一点的上浮
{
if(sort_array[j]<sort_array[j-1])
{
temp = sort_array[j-1];
sort_array[j-1] = sort_array[j];
sort_array[j] = temp;
}
}
}
}
参数大小是输入数据所占空间,函数体是程序本身所占空间,i、j、temp是辅助变量,由于i、j、temp所占大小是一定的,因此算法的空间复杂度为O(1)。
27.算法的时间复杂度和算法的空间复杂度都是按最坏情况来考虑。
时间复杂度按数量级递增排列依次为(log2n中2为底数):常数阶O(1)、对数阶O(log2n)、线性阶O(n)、线性对数阶O(nlog2n)、平方阶O(n^2)、立方阶O(n^3)、k次方阶O(n^k)、指数阶O(2^n)。