目录导读
![image-20220601164032186](https://raw.githubusercontent.com/qyhc/image/master/Blog/202206011640375.png)
![image-20220601164130990](https://raw.githubusercontent.com/qyhc/image/master/Blog/202206011641306.png)
数据概念:
早期的计算机一一只用于处理纯数值型问题
现代计算机一一经常处理非数值型问题
对于非数值型的问题:
1.我们关心每个个体的具体信息
2.我们还关心个体之间的关系
数据项、数据元素之间的关系
![image-20220601164431996](https://raw.githubusercontent.com/qyhc/image/master/Blog/202206011644191.png)
![image-20220601164541973](https://raw.githubusercontent.com/qyhc/image/master/Blog/202206011645184.png)
数据对象
- 数据对象是具有相同性质的数据元素的集合,是数据的一个子集。
- 数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
即,数据元素与数据结构之间没有指定的关心:
不同的数据类型可以组成相同的数据结构;相同的数据类型可以组成不同数据结构。
数据结构三要素
![image-20220601165138020](https://raw.githubusercontent.com/qyhc/image/master/Blog/202206011651180.png)
![image-20220601171407525](https://raw.githubusercontent.com/qyhc/image/master/Blog/202206011714631.png)
-
线性结构:
-
逻辑结构
-
物理结构–链式存储:
- 优点:不会出现碎片现象
- 缺点:每个元素因存储指针而占用额外的存储空间,且只能顺序存放
-
物理结构–索引存储:
-
优点:检索速度快
-
缺点:附加的索引表额外占用存储空间,且增减和删除数据时也要修改索引表,因此会花费较多时间
-
-
物理结构–散列存储:
-
优点:检索、增加和删除节点的操作很快
-
缺点:若散列函数不好,则可能出现元素存储单元冲突,而解决冲突会增加时间和空间的开销
-
-
-
树形结构:
-
图形结构:
-
集合结构:
数据类型、抽象数据类型
数据类型是一个值的集合和定义在此集合上的一组操作的总称。
1)原子类型。其值不可再分的数据类型
2)结构类型。其值可以再分解为若干成分(分量)的数据类型
**抽象数据类型(Abstract Data Type, ADT)😗*是抽象数据组织及与之相关的操作。
- ADT用数学化的语言定义数据的逻辑结构、定义运算。与具体的实现无关
- 确定了ADT的存储结构,才 能“实现”这种数据结构
例题
03 以下属于逻辑结构的是:
A.顺序表
B.哈希表
C.有序表
D.单链表
04 以下与数据的存储结构无关的术语是:
A.循环队列
B.链表
C.哈希表
D.栈
06 在存储数据时,通常不仅要存储各数据元素的值,而且要存储:
A.数据的操作方法
B.数据元素的类型
C.数据元素之间的关系
D.数据的存取方式
1-2 算法的基本概念
程序 = 数据结构 + 算法
算法的特性
- 有穷性。一个算法必须总在执行有穷步之后结束,且每一步都可在有穷时间内完成。
注:算法必须是有穷的,而程序可以是无穷的(如微信是程序不是算法) - 确定性。算法中每条指令必须有确切的含义,对于相同的输入只能得出相同的输出。
- 可行性。算法中描述的操作都可以通过己经实现的基本运算执行有限次来实现。
- 输入。一个算法有零个或多个输入,这些输入取自于某个特定的对象的集合。
- 输出。一个算法有一个或多个输出,这些输出是与输入有着某种特定关系的量。
好算法的特性
-
正确性。算法应能够正确地解决求解问题。
-
可读性。算法应具有良好的可读性,以帮助人们理解。
-
健壮性。输入非法数据时,算法能适当地做出反应或进行处理,而不会产生莫名其妙的输出结果。
-
高效率与低存储量需求
(时间复杂度低)+(空间复杂度低)
算法的时间复杂度
结论:
- 结论1:可以只考虑阶数高的部分
- 结论2:问题规模足够大时,常数项系数也可以忽略
运算规则:
趋向于无穷的速度::
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 ) Ο(1)<Ο(log_2n)<Ο(n)<Ο(n*log_2n)<Ο(n^2)<Ο(n^3)<Ο(2^n)<Ο(n!)<Ο(n^n) O(1)<O(log2n)<O(n)<O(n∗log2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
类型–顺序执行型:
类型–循环嵌套型:
类型–指数循环型:
类型–条件搜索型:
注:很多算法执行时间与输入的数据有关
最坏时间复杂度:最坏情况下算法的时间复杂度
平均时间复杂度:所有输入示例等概率出现的情况下,算法的期望运行时间
最好时间复杂度:最好情况下算法的时间复杂度
小节总结
例题
08 设n是描述问题规模的非负整数,下面程序片段的时间复杂度是:
x = 2; while(x<n/2) x = 2*x;
A. O ( l o g 2 n ) Ο(log_2n) O(log2n)
B. O ( n ) Ο(n) O(n)
C. O ( n l o g 2 n ) Ο(nlog_2n) O(nlog2n)
D. O ( n 2 ) Ο(n^2) O(n2)
[解]
执行频率最高的语句为
x = 2*x
每执行一次,x乘2。假设该语句最终执行t次,又因x初始值为2,所以有:2 t + 1 < n / 2 2^{t+1}<n/2 2t+1<n/2=>
( t + 1 ) l o g 2 2 < l o g 2 ( n / 2 ) (t+1)log_22<log_2(n/2) (t+1)log22<log2(n/2)=>
( t + 1 ) < l o g 2 n − 1 (t+1)<log_2n-1 (t+1)<log2n−1=>
t < l o g 2 n − 2 t<log_2n-2 t<log2n−2 因此有 T ( n ) = O ( l o g 2 n ) T(n)=Ο(log_2n) T(n)=O(log2n)
10 已知两个长度分别为 m 和 n 的升序两边,若将他们合并为长度为 m + n 的一个降序链表,则最坏情况下的时间复杂度是:
A. O ( n ) Ο(n) O(n)
B. O ( m n ) Ο(mn) O(mn)
C. O ( m i n ( m , n ) ) Ο(min(m,n)) O(min(m,n))
D. O ( m a x ( m , n ) ) Ο(max(m,n)) O(max(m,n))
[解]
一开始我的想法是,先将这两个升序链表合并成一个升序链表花费 O ( m i n ( m , n ) ) Ο(min(m,n)) O(min(m,n)) ,然后再整体使用头插法实现逆序,花费 O ( m + n ) Ο(m+n) O(m+n) 两者相加: O ( m + n + m i n ( m , n ) ) Ο(m+n+min(m,n)) O(m+n+min(m,n))
- 当 m>>n时: O ( m + n + m i n ( m , n ) ) Ο(m+n+min(m,n)) O(m+n+min(m,n))~ O ( 2 m ) Ο(2m) O(2m) ~ O ( m ) Ο(m) O(m) > > O ( n ) >>Ο(n) >>O(n)
A错;B: O ( n ) Ο(n) O(n) 故C错;而D: O ( m ) Ο(m) O(m) 满足条件
- 当 m~n时 : O ( m + n + m i n ( m , n ) ) Ο(m+n+min(m,n)) O(m+n+min(m,n)) ~ O ( n + m ) Ο(n+m) O(n+m)~ O ( 2 n ) Ο(2n) O(2n) ~ O ( n ) Ο(n) O(n)
B~ O ( n 2 ) Ο(n^2) O(n2) 因此B错;而D: O ( n ) Ο(n) O(n) ~ O ( m ) Ο(m) O(m) 满足条件
- 当 m<<n时: O ( m + n + m i n ( m , n ) ) Ο(m+n+min(m,n)) O(m+n+min(m,n))~ O ( 2 n ) Ο(2n) O(2n) ~ O ( n ) Ο(n) O(n)
而 D: O ( n ) Ο(n) O(n) 满足条件
- 综上,D满足所有情况
12 下列函数的时间复杂度是:
int func(int n){ int i = 0,sum = 0; while(sum<n) sum += ++i; return i; }
A. O ( l o g n ) Ο(logn) O(logn)
B. O ( n 1 / 2 ) Ο(n^{1/2}) O(n1/2)
C. O ( n ) Ο(n) O(n)
D. O ( n l o g n ) Ο(nlogn) O(nlogn)
[解]
基本运算 sum += ++i,等价于 ++i;sum = sum+i,每执行一次 i 自增1。
不难发现这是一个公差为1的等差数列求和,起始项为1,尾项为n,共有n项。可知循环次数满足: ( 1 + t ) ∗ t / 2 < n (1+t)*t/2<n (1+t)∗t/2<n => 时间复杂度为 O ( n 1 / 2 ) Ο(n^{1/2}) O(n1/2)
扩展:
求解 递归与非递归算法下的斐波那契数列算法时间复杂度
KaTeX parse error: No such environment: equation at position 8: \begin{̲e̲q̲u̲a̲t̲i̲o̲n̲}̲ F(n) = \begin{…
- 递归求解:
int dfs(n){ if(n<=1) return 1; return dfs(n-1)+dfs(n-2); }
执行图:
即, 2 0 + 2 1 + 2 2 + . . + 2 n = 1 − 2 n 1 − 2 = 2 n − 1 2^0+2^1+2^2+..+2^n=\frac{1-2^n}{1-2}=2^n-1 20+21+22+..+2n=1−21−2n=2n−1 故时间复杂度为: O ( 2 n ) Ο(2^n) O(2n)
- 非递归求解
int dfs(int n){ if(n<=1) return 1; int[] tmp = new int[n+1];//初始化数组 tmp[1] = 1; for(int i = 2;i<=n;i++){ //其过程只依靠与前两个元素 temp[i] = temp[i-1] + temp[i-2]; } return tmp[n]; }
不难看出,时间复杂度为 O ( n ) Ο(n) O(n)