绪论
一、信息化世界的本质
二、数据结构的基本概念
(一)数据:
信息的载体,能输入到计算机并被计算机程序所识别和处理的符号的集合;所谓计算机能够识别和处理,就是指二进制0和1。
(二)数据元素和数据项:
1.数据元素:
描述一个个体的信息,例如:一个人的财富信息:姓名,财富,财富来源等等
数据的基本单位,通常作为一个整体考虑和处理,一个数据元素可由若干个数据项组成。
2.数据项:
数据元素中每一个细分的属性
是构成数据元素的不可分割的最小单位
举例说明:
事件1:海底捞顾客的取号排队就餐事件
每一波顾客即为一个数据元素,顾客的号数,取号时间等即为数据项
事件2:微博的账户信息
每个账户即为一个数据元素,账户的昵称,性别等即为数据项
其中,出生日期数据项内包含年,月,日,故此数据项称为组合项
(三)数据对象和数据结构:
1.数据对象:
相同数据元素的集合即为数据对象,例如:微博账号为数据元素,所有微博用户的账号组合在一起则构成数据对象,即:
具有相同性质的数据元素的集合,是数据的一个子集
2.数据结构:
相互之间存在一种或多种特定关系的数据元素的集合
数据结构强调数据元素之间的关系,例如:
中国所有人的财富总数排名,财富即为数据元素,每个财富下的姓名,财富值等信息即为数据项,前400的富豪数据元素和一般人的数据元素组成数据对象,财富总数的排名采用线性关系,即为数据结构
2.1.数据结构三要素:
2.1.1.逻辑结构:
1.集合结构:
同属一个集合:
例如:
2.线性结构(一对一):
除了第一个元素,其他元素均有唯一前驱;除了最后一个元素,其他元素均有唯一后继
例如:
3.树型结构(一对多):
例如:
4.图状结构(多对多):
例如:
2.1.2.数据的运算:
针对某种数据结构,结合实际需求,定义其基本运算(基本操作)
例如:针对线性结构,基本运算有:
查找第i个元素;在第i个位置插入新元素;删除第i个位置的数据元素等
2.1.3.物理结构(存储结构):
反应数据存储的逻辑关系
1.顺序存储:
把逻辑上相邻的元素存储在物理位置上也相邻的存储单元
2.链式存储:
逻辑上相邻的元素在物理位置上不相邻,借助指示元素存储地址的指针来表述元素之间的逻辑关系
3.索引存储:
存储元素的同时,建立索引表,索引表中的每项称为索引项,索引项的一般格式:(关键字,地址)
4.散列存储(哈希(hash)存储):
根据元素的关键字直接计算出该元素的存储地址
5.存储结构总结:
数据的存储结构影响数据空间分配的方便程度
数据的存储结构影响对数据的运算速度
补充内容:
1.数据类型:
一个值的集合和定义在此集合上的一组操作的总称
1)原子类型:其值不可再分的数据类型,例如bool,int等类型
2)结构类型:值可以分解为若干分量的数据类型,例如结构体类型
2.抽象数据类型:
(abstract data type),ADT 抽象数据组织及与之相关的操作,对一个数据结构逻辑特性的描述
三、算法
(一)概念:
程序 = 数据结构 + 算法
数据结构:如何用数据正确的描述现实世界的问题,并存入计算机;
算法:如何高效的处理数据,以解决实际问题。是对特定问题求解步骤的一种描述,是指令的有限序列。
例如:
数据结构是食材,存入解决问题的数据;算法是步骤,完成解决问题的求解步骤
(二)算法的特性:
1.有穷性:
一个算法必须总在执行有限个步骤后结束,并且每一步都可以在有限时间内完成。
注:
1.1.算法必须是有穷的,但是程序是无穷的,微信是一个程序,可以无穷尽的运行下去!
1.2.死循环不是算法,不满足有穷性!
2.确定性:
算法中每条指令必须有确切的含义,对于相同的输入必须有相同的输出;每条指令的规则明确无歧义。
3.可行性:
算法中描述的操作都可以通过已经实现的基本运算执行有限次来实现。
4.输入:
一个算法有零个(比如hello word文本打印)或多个输入。
5.输出:
一个算法有一个或多个输出,输入输出有某种特定关系,比如y = f(x)函数!
(三)“好算法”具备的特征(追求的目标):
1.正确性:
正确求解问题。
2.可读性:
帮助人理解。
3.健壮性:
也称为鲁棒性,当输入非法数据时,算法能适应并给出反应或进行处理,而不会产生莫名其妙的结果。
4.高效率与低存储量需求:
高效率:花的时间少,时间复杂度低;低存储量:不费内存,空间复杂度低。
四、算法的时间复杂度
注:评判算法的时间开销:
1.事后统计法:让算法运行,事后统计时间
存在问题:I.和机器性能有关;II.和编程语言有关,越高级的语言效率越低;III.有的算法不能事后统计,比如导弹控制算法;IV.和编译程序产生的机器指令质量有关。
2.事前估计法:排除与算法本身无关的外界因素
时间开销T(n)与问题规模n的关系(T = time)。
(一)C语言示例:
void loveyou(int n){//n为问题的规模
1 int i = 1;
2 while(i <= n){
3 printf("I Love You %d 遍!\n",i);
4 i++;
}
5 printf("I Love You Move That %d 遍!\n",i);
}
int main(){
loveyou(3000);
}
语句频度(每句算法的执行次数):
1---------------------------------1次
2---------------------------------(n+1)次,最后一个用于条件判断的跳出(即3001次)
3---------------------------------n次(即3000次)
4---------------------------------n次(即3000次)
5--------------------------------1次
T(3000) = 1 + 3001 + 2*3000 + 1
则时间开销与问题规模n的关系为:
T(N) = 3n + 3
问题一:能否简化表达式:忽略表达式中低阶部分与高阶的系数部分,只考虑高阶,用O(n)表示。
加法规则:不同阶数相加,只保留最高阶项,且系数变为1.T(n) = O(f(n)) + O(g(n)) = O(max(f(n),g(n)))
乘法规则:多项相乘,都保留,T(n) = O(f(n)) x O(g(n)) = O(f(n)xg(n))
常用时间复杂度的阶数排序:
口诀:常对幂指阶
问题二:能否只看算法的某些部分: 顺序执行代码可忽略,只需挑出循环中的一个基本操作分析即可
问题三:嵌套循环如何分析:若外层循环执行n次,内层循环执行n2次,则结果为n2,做和即可,即若有多层循环,只需要关注最内层的循环循环了几次!
(二)练习:
void loveyou(int n){
int i = 1;
while(i <= n){
i = i * 2;
printf("I Love You %d 遍!\n",i);
}
printf("I Love You Move That %d 遍!\n",i);
}
时间复杂度:
1.找最深循环语句:i = i * 2;
2.设最深处的循环总次数为x,则有一下分析:
循环总次数x 1 2 3 … n
i的值 2 4 8 … 2n
3.则当循环x次时,i 为2x,即根据条件i <= n,当不刚刚不满足条件时为:2x > n
4.故x = log2n + 1,即T(n) = O(log2n)
//flag[n]中乱序存放1~n这些数,遍历找到等于n的数
void loveyou(int flag[],int n){
printf("I Am Iron Man!\n");
for(int i=0;i<n;i++){
if(flag[i] == n){
printf("I Love You %d\n",n);
break;
}
}
}
时间复杂度:
最好情况:n在第一个元素 T(n) = O(1)
最坏情况:n在最后一个元素 T(n) = O(n)
平均情况:n在任意位置,概论同为1/n, T(n) = O(n)
平均情况的解释:循环次数x = (1+2+3+4+…+n.)1/n = (1+n)/2
五、算法的空间复杂度
(一)C语言示例:
void loveyou(int n){//n为问题的规模
int i = 1;
while(i <= n){
printf("I Love You %d 遍!\n",i);
i++;
}
printf("I Love You Move That %d 遍!\n",i);
}
int main(){
loveyou(3000);
}
算法装入内存中的大小是固定的,与问题规模无关
当代码运行时,内存中除了程序代码外,还需要数据的存放,即i的值,参数n等。
此无论问题规模怎么变,算法运行的内存空间都是固定的常量,为常数阶,故:S(n) = O(1) [S:space],称此代码为原地工作。
void test(int n){
int flag[n];
int i;
....
}
假设一个int占4个字节,则:
int n 占 4,int flag[n] 占 4n,int i 占 4,共4n + 8,则S(n) = O(4n+8) = O(n)
void test(int n){
int flag[n][n];
int i;
}
int n,int i不需要考虑,int falg[n][n]占用空间为4n2,则S(n) = O(n2)
int flag[n][n];
int other[n];
int i;
S(n) = O(n2) + O(n) + O(1) = O(n2)
void loveyou(int n){
int a,b,c;
if(n > 1){
loveyou(n-1);
}
}
int main(){
loveyou(5);
}