1.数据
数据元素是一个基本的数据单位,通常是一个小整体,一个数据元素可以由多个数据项组成,数据项是构成数据元素不可分割的最小单位。多个性质相同的数据元素可以组成一个数据对象。
比如,小明同学的信息包括了学号,姓名,班级三个,那这三个就是数据项,小明同学作为一个数据元素包含这三个数据项。多个同学加上老师可以构成一个班级管理系统,这就是一个数据对象。
数据结构则是用于处理数据元素之间关系的工具。
比如我们要找到学生的信息,那么学生都有特定的联系,这时候就可以用数据结构来遍历,除去老师,因为老师虽然和学生在同一个数据对象中,但是关系并不一致。
同一个数据对象里面,可以存在着多种数据结构,来表示不同的关系。
2.数据结构的四大逻辑结构
2.1 集合结构
各个数据元素在同一个集合里面。比如一个班的花名册,排名不分先后。
2.2 线性结构
每一个元素之间呈现一个一对一的线性的关系,有前驱和后继。比如班级排名。
2.3 树形结构
每一个元素和多个数据元素之间有关系,一对多。比如一个班级对应多个班级职位,而一个班级职位又对应了多个学生。
2.4 网状结构
多个元素对应多个元素,多对多。比如一个班里面,每一个同学都会有多个好友。
3.对数据的运算
就是对数据的操作方法,简单介绍一下线性结构的运算:
1.查找第i个数据元素。
2.在第i个位置插入元素。
3.删除第i个位置的元素。
4.实现数据结构
前面说的是定义,它不同于实现,要实现有多种方法。存储方式,一共有四种,分别是顺序存储,链式存储,索引存储和散列存储(哈希存储)。后面章节都会细谈。
不同的实现方法都会有自己的优点和缺点(会影响运算的速度和存储的方便程度),需要根据实际的需求去衡量。
定义加实现,就可以完成一个数据结构。
5.数据类型和抽象数据类型
5.1数据类型
数据类型分为原子类型和结构类型。原子类型不可再分,比如bool类型,int类型。结构类型是可以分解的,比如一个strcut结构体。
5.2 抽象数据类型(ADT)
ADT其实就是数据结构,因为如果你把多个数据元素的关系集合看作一个整体,我们可以称它为一种类型,比如一个班级可以作为一个类型。
6.算法
程序 = 数据结构 + 算法
算法就是对数据的操作方法。
6.1 算法的五大特性
1.有穷性
它要可以运行完。
2.确定性
对于相同的输入,只能得到相同的输出。
3.可行性
必须可以用代码实现。
4、5.输入和输出
算法可以没有输入,也可以有一个或者多个输入,但是必须有一个或多个输出。算法和函数非常相似。
6.2 好算法的特征
1.正确性
能对正常的输入给出正确输出。
2.有可读性
不管是伪代码,还是C语言,至少要自己看得懂。
3.健壮性
可以处理非法数据。
4.时空复杂度低
7.时空复杂度的计算
7.1 时间复杂度的计算
直接用电脑运行则和机器性能相关,和编程语言相关所以,只靠运行则会导致测不准。
1.时间复杂度的计算
于是我们把算法的时间开销称为T,解决问题的规模称为n,所以就是找T关于n的函数T(n)。那么我们就可以用算法中执行代码的行数的数量来和n的关系来表示时间复杂度。比如下面这样的代码:
Test(int n){
1.int i = 0;
2.while(i<n){
3.i++;
4.cout<<"hello"<<endl;}
}
在这段代码中,1会运行1次,2的判断会运行n+1次(因为最后要多一次判断),3和4均会运行n次,故T(n)=3n+2。
2.时间复杂度的简化表示
当T(n)= 3 n 3 + n 2 3n^3 + n^2 3n3+n2的时候,我们可以用等价无穷大的思想,把表达简化,我们仅表示其等价无穷大,用O(n)表示,所以我们可以写成O(n)= n 3 n^3 n3。
3.时间复杂度的运算
加法规制:
T
(
n
1
)
+
T
(
n
2
)
=
m
a
x
(
O
(
f
(
n
)
)
,
O
(
g
(
n
)
)
)
T(n_1)+T(n_2)=max(O(f(n)),O(g(n)))
T(n1)+T(n2)=max(O(f(n)),O(g(n)))(多项式相加,直接选最大的)
乘法规则:
T
(
n
1
)
×
T
(
n
2
)
=
O
(
f
(
n
)
×
O
(
g
(
n
)
)
)
T(n_1)\times T(n_2)=O(f(n)\times O(g(n)))
T(n1)×T(n2)=O(f(n)×O(g(n)))(多项式乘法,留下最大的)
4.时间复杂度排行
非常重要,背这里就行:
注意,算法的log没有特殊说明,都是以2为底的。
O
(
1
)
<
O
(
l
o
g
n
)
<
O
(
n
)
<
O
(
n
l
o
g
n
)
<
O
(
n
2
)
<
O
(
n
3
)
<
O
(
2
n
)
<
O
(
n
!
)
<
O
(
n
n
)
O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n)
O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
记住公式:常对幂指阶
实在不行就做一个除法,比较两个在n趋近于无穷的时候的极限
l
i
m
n
−
>
∞
f
(
n
)
g
(
n
)
lim_{n->∞} \frac{f(n)}{g(n)}
limn−>∞g(n)f(n),用洛必达洛就行。
5.计算时间复杂度
因为顺序执行的部分都是常数,所以我们在计算代码的时间复杂度的时候,我们只需要计算一下循环里面的执行次数就行,比如一层循环是O(n),两层嵌套是O(
n
2
n^2
n2),面对多层循环嵌套时,我们选择最内层的去算即可。
对于输入的不同,有时候时间复杂度不是一定的,我们主要按照最坏的情况或者平均情况去算,所以只需要算平均和最坏情况即可。
6.空间复杂度计算
6.1 程序运行时需要的内存
(1)需要把整个程序都导入内存中,所以需要固定存放所有代码的数据,但是这个往往不需要考虑。
(2)运行时所需要和产生的局部变量以及参数,计算空间复杂度所计算的就是这部分的内容。
空间复杂度一样用英文的首字母表示为S(n)。如果空间复杂度为常数(S(n)=O(1))则可以称为该算法能够原地工作。
6.2 空间复杂度的计算
和时间复杂度一样,如果代码的参数不存在变量时,它就是常数级别。如果存在变量,则只需要注意运行时,变量的数目大小即可,并且往往取其同阶的最小无穷大级数O即可。循环分析一轮即可,但是递归需要考虑每一轮。