一、数据、数据元素、数据项、数据对象
1、数据
含义:客观事物的符号表示,是所有能够输入到计算机中并被计算机程序处理的符号总称
举例:① 数学计算中的整数和实数
② 文本编辑用到的字符串
③ 多媒体程序处理的图形、图像声音及动画等经过特殊编码定义后的数据
思考:① 在计算机底层数据只由0和1组成
② 正常来说我们能看到的数据的基本组成元素是数字和字符
③ 这里的数据是宏观定义,主要用途是方便表示和描述各种事物
2、数据元素
含义:是数据的基本单位,在计算机中通常作为一个整体进行考虑和处理
举例:① 图书管理系统中,一本书就是一个数据元素
② 学生信息管理系统中,一个学生就是一个数据元素
③ 电商平台后台数据中,一件商品就是一个数据元素
思考:① 是数据的具体描述,但不是最具体的描述,也不是很宽泛的描述
② 不同情况下数据元素的称谓不同,如元素、记录、结点、顶点等
③ 数据元素用于完整地定义、标识、表示、描述一个对象
3、数据项
含义:是组成数据元素的、有独立含义的、不可分割的最小单位
举例:① 图书管理系统中,一本书的书名、作者、出版日期、ISBN号等都是数据项
② 学生信息管理系统中,一个学生的姓名、性别、学号、班级等都是数据项
③ 电商平台后台数据中,一件商品的名称、价格、库存量、描述等都是数据项
思考:数据项就是数据元素所具有的属性
4、数据对象
含义:是性质相同的数据元素的集合,是数据的一个子集
举例:① 整数集合、有理数集合、中文字符集合
② 学生信息表、后台数据库表
③ 企业管理系统中,所有员工的资料构成一个数据对象,其中一个员工的记录代表一个数
据元素
思考:① 无论集合是否有限
② 集合内元素必须有相同性质
四者关系图如下:
二、数据结构
1、数据结构
含义:是相互之间存在一种或多种特定关系的数据元素的集合
包括:① 逻辑结构 ② 存储结构
2、数据的逻辑结构
含义:数据的逻辑结构是从逻辑关系上描述数据,它与数据的存储无关,是独立于计算机的
两个要素:① 数据元素(含义如前所述)
② 关系(指数据元素之间的逻辑关系)
四种基本结构:
① 集合结构
含义:数据元素之间除了属于同一集合的关系外,别无其他关系
举例:整数集合,集合里面的数字除了属于整数这个集合,没有其他关系
② 线性结构
含义:数据元素之间存在一对一的关系
举例:按照成绩高低排列学生信息,这个排列将组成一个线性结构
③ 树结构
含义:数据元素之间存在一对多的关系
举例:一个公司,老板管理所有部门负责人,负责人管理自己手下员工
④ 图结构或网状结构
含义:数据元素之间存在多对多的关系
举例:一个班级,任意两位同学之间的朋友关系
非线性结构:集合结构、树结构、图结构或网状结构(除了线性结构其它肯定就是非线性的了)
线性结构:① 线性表(典型的线性结构,比如学生基本信息表)
② 栈和队列(具有特殊限制的线性表,数据操作只能在表的一端或两端进行)
③ 字符串(特殊的线性表,数据元素仅由一个字符构成)
④ 数组(线性表的推广,数据元素是一个线性表)
⑤ 广义表(线性表的推广,数据元素是一个线性表,但不同构,要么单元素,要么线
性表)
3、数据的存储结构
含义:数据对象在计算机中的存储表示称为数据的存储结构,也称为物理结构
要点:把数据对象存储到计算机时,既要求存储各数据元素的数据,又要求存储数据元素之间的逻
辑关系
两种基本结构:
① 顺序存储结构
含义:借助元素在存储器中的相对位置来表示数据元素之间的逻辑关系
特点:所有元素依次存放在一片连续的存储空间
表示:通常使用数组表示
② 链式存储结构
含义:用一组任意的存储单元存放数据元素,这组存储单元可以是连续的,也可以是不连续的
特点:可以不需要占用一片连续的存储空间
表示:将数据元素称作结点,每个结点占用两个连续的存储单元,存储单元中第一个单元存放
结点信息,第二个单元存放指针,这个指针存储后继结点的首地址,借助指针表示元素
之间的逻辑关系
4、数据类型
含义:是一个值的集合和定义在这个值集上的一组操作的总称
解释:① 数据类型是高级程序设计语言的一个基本概念
② 在程序设计语言中,每一个数据都属于某种数据类型
③ 类型明显或隐含地规定了数据的取值范围、存储方式以及允许进行的运算
举例:C语言中提供的整型、浮点型、字符型等基本数据类型
5、抽象数据类型
含义:一般指由用户定义的、表示应用问题的数学模型,以及定义在这个模型上的一组操作的总称
组成:① 数据对象
② 数据对象上关系的集合
③ 对数据对象的基本操作的集合
实现:类是C++中实现抽象数据类型的主要机制
举例:① 对于栈来说,我们不需要关心其内部是如何实现的(例如,它可能使用数组或链表作为
底层数据结构),只需要知道它提供的接口,如push(入栈)、pop(出栈)、peek
(查看栈顶元素)等。这使得栈的使用更加简单和直观,同时也提高了代码的复用性和可
维护性
② 类似的抽象数据类型还有线性表、队列、树、图
作用:抽象数据类型的主要目的是将数据的表示与数据的操作分离开来,使得用户只需关心数据的
行为,而不必关心数据在计算机中的具体存储形式,这种分离使得代码更加模块化和易于维
护,同时提高了代码的可重用性
三、算法和算法分析
1、算法
定义:算法是为了解决某类问题而规定的一个有限长的操作序列
特性:① 有穷性:一个算法必须总是在执行有穷步后结束,且每一步都必须在有穷时间内完成
② 确定性:对于每种情况下所应执行的操作,在算法中都有确切的规定,不会产生二义
性,使算法的执行者或阅读者都能明确其含义及如何执行
③ 可行性:算法中的所有操作都可以通过已经实现的基本操作运算执行有限次来实现
④ 输入:一个算法又零个或多个输入
⑤ 输出:一个算法有一个或多个输出
评价算法优劣的四大基本标准:
① 正确性:在合理的数据输入下,能够在有限的运行时间内得到正确的结果
② 可读性:一个好的算法,首先应该便于人们理解和相互交流,其次才是机器可执行性。可读性
强的算法有助于人们对算法的理解,而难懂的算法易于隐藏错误,且难于调试和修改
③ 健壮性:当输入的数据非法时,好的算法能适当地做出正确反应或进行相应处理,而不会产生
一些莫名其妙的输出结果
④ 高效性:高效性包括时间和空间两个方面,时间高效是指算法设计合理,执行效率高,可以用
时间复杂度来度量。空间高效是指算法占用存储容量合理,可以用空间复杂度来度量
时间复杂度和空间复杂度是衡量算法的两个主要指标
2、算法的时间复杂度
衡量算法效率的方法:事后统计法和事前分析估算法
问题规模:不考虑计算机的软硬件等环境因素,影响算法时间代价的最主要因素是问题规模,问题
规模是算法求解问题输入量的多少,是问题大小的本质表示,一般用整数n表示,问题
规模n对不同的问题含义不同,例如,在排序运算中n为参加排序的记录数,在集合运算
中n为集合中元素的个数,在树的有关运算中n为树的结点个数,显然,n越大算法的执
行时间越长
语句频度:一条语句的重复执行次数
注意:所谓的算法分析并非精确统计算法实际执行所需时间,因为算法的实际执行时间是与机器
软硬件环境密切相关的,同一算法在不同环境下执行可能出现差异,因此我们只针对算法
中语句的执行次数做出估计,从而得到算法执行时间的信息
算法执行时间:设每条语句执行一次所需要的时间均是单位时间,则一个算法的执行时间可以用
该算法中所有语句频度之和来度量
计算算法执行时间举例:
解析:第一条for语句:语句频度是n+1,因为会进行n+1次判断
第二条for语句:语句频度是n*(n+1),因为第一条for语句每一次判断条件成立后,第二条for
语句都会执行n+1次判断,而第一条for语句判断条件成立的次数是n次,所
以就是第一条for语句的n次*第二条for语句的n+1次
c[i][j]=0语句:语句频度是n^2,因为只有前两个for语句判断条件都成立的情况下才可能执行
该语句,因此就是n*n
依次类推后面的语句频度都是如此分析,那么该算法的执行时间就等于所有语句频度之和
基本语句:指的是算法中重复执行次数和算法的执行时间成正比的语句,它对算法运行时间的贡献
最大
渐近时间复杂度:一般情况下,算法中基本语句重复执行的次数是问题规模n的某个函数f(n),算法
的时间量度记作T(n)=O(f(n)),它表示随问题规模n的增大,算法执行时间的增长
率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称时间复杂度
什么是时间复杂度?
时间复杂度是一个关于算法运行时间长短的度量,它定量描述了算法的执行时间随输入规模增长而
变化的趋势。在计算机科学中,算法的时间复杂度通常定义为算法所需的计算时间与问题规模之间
的关系。具体来说,如果某个问题的规模是n,解这一问题的某一算法所需要的时间为T(n),那么
T(n)就是该算法的时间复杂性。
算法时间复杂度的分析方法:
① 找出所有语句中语句频度最大的那条语句作为基本语句
② 计算基本语句的频度得到问题规模n的某个函数f(n)
③ 取其数量级用符号"O"表示即可
求非递归算法的时间复杂度举例:
① 常量阶
{
x++;
s = 0;
}
两条语句的语句频度都是1,算法的时间复杂度为T(n)=O(1),因为算法的执行时间与问题规模n无
关
for (i = 0; i < 10000; i++) {
x++;
s = 0;
}
两条语句的语句频度都是10000,但是算法的时间复杂度仍然为O(1)
结论:如果算法的执行时间不随问题规模n的增加而增长,算法中语句频度就是某个常数。即使
这个常数再大,算法的时间复杂度都是O(1)
② 线性阶
for (i = 0; i < n; i++) {
x++;
s = 0;
}
循环体内两条基本语句的语句频度均为f(n)=n,所以算法的时间复杂度为T(n)=O(n)
③ 平方阶
x = 0;
y = 0;
for (k = 1; k <= n; k++)
x++;
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
y++;
以上程序频度最大的语句是第7行y++,其频度为f(n)=n^2,所以该算法的时间复杂度T(n)=O(n^2)
④ 立方阶
x = 1;
for (i = 1; i <= n; i++)
for (j = 1; j <= i; j++)
for (k = 1; k <= j; k++)
x++;
我们可以非常明显的看出该程序段中频度最大的语句是第5行的x++,但是因为从第2个for语句开始
的判断条件都是和上一个for语句相关,即判断j<=i必须先知道i的值,判断k<=j必须先知道j的值,因
此如果我们从上到下去计算x++的语句频度会比较困难,所以我们使用逆向思维,从下往上去计算
x++的语句频度,计算过程如下:
首先,只看第三个for语句,我们很容易得出x++的语句频度是j,因为循环执行了j次,
接着,我们将第二个for语句加入,则可以知道j是从1增加i的,所以用求和公式进一步计算出x++的
语句频度,等于
最后,我们把第一个for语句加入,则可以知道i是从1增加到n,因此依然使用求和公式计算x++的
语句频度,等于
计算到这里就不把式子展开然后合并同类项了,因为我们一眼就容易看出最高次幂是3次,所以该
程序的时间复杂度是T(n)=O(n^3)
⑤ 对数阶
for (i = 1; i <= n; i = i * 2) {
x++;
s = 0;
}
常见的时间复杂度按数量级递增排列依次为:
不同数量级的时间复杂度性状如下图
一般情况下,随着n的增大,T(n)的增长越慢那么算法越优,从图中我们容易得出应该尽量避免使
用指数阶,因为指数阶的增长是爆炸性增长,n值稍大一点程序的运行时间就过长,算法效率极低
注意:通常情况下我们讨论的时间复杂度是最坏时间复杂度
3、算法的空间复杂度
什么是空间复杂度?
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。记作S(n)=O(f(n)),其中n为
问题的规模或大小,空间复杂度同样表示的是随问题规模n的增大,算法临时占用存储量的增长情
况,因而也用大O数量级来表示。
举例:
算法1
for (i = 0; i < n / 2; i++) {
t = a[i];
a[i] = a[n - i - 1];
a[n - i - 1] = t;
}
该算法需要另外借助一个变量t,与问题规模n大小无关,所以空间复杂度为O(1)
算法2
for (i = 0; i < n; i++)
b[i] = a[n - i - 1];
for (i = 0; i < n; i++)
a[i] = b[i];
该算法需要另外借助一个大小为n的辅助数组b,所以空间复杂度为O(n)
4、总结
对于一个算法,其时间复杂度和空间复杂度往往是相互影响的,当追求一个较好的时间复杂度时,
可能会导致占用较多的存储空间,即可能会使空间复杂度性能变差,反之亦然。不过,通常情况
下,鉴于运算空间较为充足,人们都以算法的时间复杂度作为算法优劣的衡量指标