第一章 数据结构-绪论与算法
一、绪论
1.数据结构的基本概念
1.1 数据元素、数据项
数据元素是数据的基本单位,通常作为一个整体考虑和处理。
一个数据元素可由若干个数据项组成,数据项是构成数据元素的不可分割的最小单位。
1.2 数据对象
数据对象是具有相同性质的数据元素集合,是数据的一个子集。
数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
2.数据结构的三要素
2.1 逻辑结构
·线性数据结构:数据元素之间是一对一的关系;除第一个元素,所有元素都有唯一的前驱;除最后一个元素,所有的元素都有唯一的后继。
·树形结构:数据元素之间是一对多的关系。
·图结构(网状结构):数据元素之间是多对多的关系。
·集合结构
2.2 数据的运算
针对某种逻辑结构,结合实际需求,定义基本运算。
2.3 物理结构(存储结构)
·顺序存储:把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。
·链式存储:逻辑上相邻的元素在物理地址上可以不相邻,借助指示元素存储的地址指针来表示元素之间的逻辑关系。
·索引存储:在存储元素信息的同时,还建立附加的索引表。索引表中的每项称为索引项。索引项的一般形式是(关键字,地址)。
·散列存储:根据元素的关键字直接计算出该元素的存储地址,又称哈希(Hash)存储。
2.3 概念
若采用顺序存储,则各个数据元素在物理上必须是连续的;若采用非顺序存储,则各个数据元素在物理上可以使离散的。
数据的存储结构会影响存储空间分配的方便程度。
数据的存储结构会影响对数据运算的速度。
运算的定义是针对逻辑结构的,指出运算的功能。
运算的实现是针对存储结构的,指出运算的具体操作步骤。
3.数据类型、抽象数据类型
3.1 数据类型
数据类型是一个值的集合和定义在次集合上的一组操作的总称。
(1)原子类型:其值不可再分的数据类型。例如bool,int。
(2)结构类型:其值可以在分解为若干成分(分量)的数据类型。例如struct。
3.2 抽象数据类型
**抽象数据类型(ADT)**是抽象数据组织及与之相关的操作。
二、算法
1.算法的基本概念
1.1 算法定义
程序=数据结构+算法
算法是对特定问题求解步骤的一种描述,他是指令的有限序列,其中的每条指令表示一个或多个操作。
1.2 算法的特性
(1)有穷性:一个算法必须总在执行有穷步之后结束,且每一步都可在有穷时间内完成。(ps:算法必须是有穷的,程序可以是无穷的)。
(2)确定性:算法中每条指令必须有确切的含义,对于相同的输入只能得出相同的输出。
(3)可行性:算法中描述的操作都可以通过已经实现的基本运算执行有限次来实现。
·输入:一个算法有零个或多个输入
·输出:一个算法有一个或多个输出
1.3 “好”算法的特质
(1)正确性:算法能正确的解决求解问题。
(2)可读性:算法应具有良好的可读性。
(3)健壮性:输入非法数据时,算法能适当地做出反应或进行处理。
(4)高效率与低存储需求:较少的时间复杂度和空间复杂度。
2.算法的时间复杂度
2.1 如何计算
可以只考虑阶数高的部分
大O表示“同阶”,等同数量级。即当 n → ∞ n \rightarrow \infty n→∞ 时,二者之比为常数。
算法 | 时间复杂度 |
---|---|
T 1 ( n ) = 3 n + 3 T_1(n)=3n+3 T1(n)=3n+3 | O 1 ( n ) = n O_1(n)=n O1(n)=n |
T 2 ( n ) = n 2 + 3 n + 1000 T_2(n)=n^2+3n+1000 T2(n)=n2+3n+1000 | O 2 ( n ) = n 2 O_2(n)=n^2 O2(n)=n2 |
T 3 ( n ) = n 3 + 3 n + 9999999 T_3(n)=n^3+3n+9999999 T3(n)=n3+3n+9999999 | O 3 ( n ) = n 3 O_3(n)=n^3 O3(n)=n3 |
2.2 常用技巧
(1)加法规则:
T ( n ) = T 1 ( n ) + T 2 ( n ) = O ( f ( n ) ) + O ( g ( n ) ) = O ( m a x ( f ( n ) , g ( n ) ) ) T(n)=T_1(n)+T_2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n))) T(n)=T1(n)+T2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n)))
多项相加,只保留最高阶的项,且系数变为1。
(2)乘法规则:
T ( n ) = T 1 ( n ) × T 2 ( n ) = O ( f ( n ) ) × O ( g ( n ) ) = O ( f ( n ) × g ( n ) ) T(n)=T_1(n) \times T_2(n)=O(f(n)) \times O(g(n))=O(f(n) \times g(n)) T(n)=T1(n)×T2(n)=O(f(n))×O(g(n))=O(f(n)×g(n))
E g : T 3 ( n ) = n 3 + n 2 l o g 2 n = O ( n 3 ) + O ( n 2 l o g 2 n ) = O ( n 3 ) Eg: T_3(n)=n^3+n^2log_2n=O(n^3)+O(n^2log_2n)=O(n^3) Eg:T3(n)=n3+n2log2n=O(n3)+O(n2log2n)=O(n3)
多项相乘,都保留。
(3)结论:
· O ( 1 ) < O ( l o g 2 n ) < O ( n ) < O ( n l o g 2 n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( n n ) O(1) \lt O(log_2n) \lt O(n) \lt O(nlog_2n) \lt O(n^2) \lt O(n^3) \lt O(2^n) \lt O(n!) \lt O(n^n) O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
·顺序执行的代码只会影响常数项,可以忽略。
·只需要挑循环中的一个基本操作分析它的执行次数与n的关系即可。
·如果多层嵌套循环,只需要关注最深层循环了几次。
2.3 三种复杂度
(1)最坏时间复杂度:考虑输入数据“最坏”的情况。
(2)平均时间复杂度:考虑所有输入数据都等概率出现的情况。
(3)最好时间复杂度:考虑输入数据“最好”的情况。
3.空间复杂度
只需要关注存储空间大小于问题规模相关的变量。
**$Eg1. $**
void test(int n){
int flag[n];
int i;
}
S ( n ) = O ( n ) S(n)=O(n) S(n)=O(n)
$Eg2. \quad $
void test(int n){
int flag[n][n];
int other[n];
int i;
}
S ( n ) = O ( n 2 ) + O ( n ) + O ( 1 ) = O ( n 2 ) S(n)=O(n^2)+O(n)+O(1)=O(n^2) S(n)=O(n2)+O(n)+O(1)=O(n2)
E g 3. Eg3. Eg3.
void loveYou(int n){
int a,b,c;
if(n > 1){
love(n-1);
}
printf("I Love You %d\n",n);
}
other[n];
int i;
}
$S(n)=O(n^2)+O(n)+O(1)=O(n^2)$
**$Eg3.$**
```c
void loveYou(int n){
int a,b,c;
if(n > 1){
love(n-1);
}
printf("I Love You %d\n",n);
}
S ( n ) = O ( n ) S(n)=O(n) S(n)=O(n) 空间复杂度=递归调用的深度
本文为个人学习笔记,若有问题可一起讨论学习
更多内容可以关注个人博客:Ackow的博客