基本术语
- 数据元素是集合,由多个数据项构成。
- 数据项是构成数据元素的不可分割的最小单位
- 数据对象是具有相同性质的数据元素的集合(如整数集合 N N N)
- 数据结构是相互之间存在一种或多种特定关系的数据元素的集合
- 数据运算的定义是针对逻辑结构的,实现是针对存储结构的。
- 算法是对特定问题的求解步骤的描述,是指令的有限序列
- 算法具有五大特性(可行性、正确性、有穷性、输入、输出)
- 正确性是指相同的输入有相同的输出
- 有穷是有限次操作,也要求有限的时间内完成
- 一个好的算法应该考虑达到:正确性、可读性、健壮性、效率与低存储量要求
基本推论
- 链式存储,结点内的储存单元地址一定连续
- 存储数据时,不仅要存储数据元素的值,还要存储数据元素之间的关系。
- 算法时间复杂度 O ( n 2 ) O(n^2) O(n2)表示该算法的执行时间和 n 2 n^2 n2成正比
- 原地工作的算法所需辅助空间为常量,即 O ( 1 ) O(1) O(1),不是不需要额外空间
- 时间复杂度排序 O ( 1 ) < O ( log 2 n ) < O ( n ) < O ( n log 2 n ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( n n ) O(1)<O(\log_2n)<O(n)<O(n\log_2n)<O(n^2)<O(n^3)<O(2^n)<O(n!)<O(n^n) O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
- 同一个算法,实现的语言级别越高,执行效率越低
- 时间复杂度是指最坏情况下估算算法执行时间的一个上界
其他辨析
逻辑结构
经典划分是两类:线性结构和非线性结构
根据数据元素之间的关系来看:
名称 | 定义 |
---|---|
集合 | 结构中的数据元素之间除同属一个集合外别无关联 |
线性结构 | 结构中的数据元素之间只存在一对一的关系 |
树形结构 | 结构中的数据元素之间存在一对多的关系 |
图 | 结构中的数据元素之间存在多对多的关系 |
集合一般是非线性结构。
有序表和顺序表和线性表
顺序表:
- 物理空间的连续
- 例如数组
- 顺序表和链表是一组相对的概念。就好像顺序存储和链式存储是相对的一组概念一样。
线性表:
- 表示的一组数据在逻辑上具有前驱和后继的关系
- 实现可以是顺序表也可以是线性表
- 直观特点是可以通过index访问
- 显然kv对不是线性表
有序表:
- 首先它是个线性表
- 其次它的元素按某种逻辑指定的顺序排序(这意味着它的排序算法有可能需要你传入一个比较函数)
定义完整数据结构
定义完整数据结构的一定是ADT(抽象数据类型):
ADT = (D, S, P)
D: 数据对象
S: D上的关系集
P: D上的操作集
即数据、数据关系和基本运算(详见我的[CS61A]Week03笔记1)
有序表属于逻辑结构
上面说了吧,有序表是线性表的一种。线性表的实现有顺序存储和链式存储。
主要是它只强调了逻辑关系,即有序。但是要说“顺序表是逻辑结构”就片面了。因为它既强调了逻辑关系,也指明了存储结构。
数据的逻辑结构独立与存储结构
这话不能反过来说。不知道逻辑结构是无法确定存储结构的。并且因为一种逻辑结构可以对应很多种物理结构,所以称之为独立。
相同的逻辑结构在不同的存储方式下的效率差异
线性表在顺序存储时的插入和删除平均时间复杂度 O ( n ) O(n) O(n),但是链式存储只要 O ( 1 ) O(1) O(1)
时间复杂度
多循环且内层循环有上界在变就用多 ∑ \sum ∑式子按部就班求
for(i = n - 1; i > 1; i--){
for(j = 1; j < i; j++){
if(A[j]>A[j+1])swap(A[j], A[j+1]);
}
}
其中n为做正整数,则swap
语句的频度在最坏的情况下为: O ( n 2 ) O(n^2) O(n2)
解:当所有元素均逆序则都每次均执行
T ( n ) = ∑ i = 2 n − 1 ∑ j = 1 i − 1 1 = ∑ i = 2 n − 1 ( i − 1 ) = ( n − 2 ) ( n − 1 ) / 2 = O ( n 2 ) T(n)=\sum_{i=2}^{n-1}\sum_{j=1}^{i-1}1=\sum_{i=2}^{n-1}(i-1)=(n-2)(n-1)/2=O(n^2) T(n)=i=2∑n−1j=1∑i−11=i=2∑n−1(i−1)=(n−2)(n−1)/2=O(n2)
for(i = 1; i <= n; i++)
for(j = 1; j <= i; j++)
for(k = 1; k <= j; k++)
x++;
∑ i = 1 n ∑ j = 1 i ∑ k = 1 j 1 = ∑ i = 1 n ∑ j = 1 i j = ∑ i = 1 n i ( i + 1 ) 2 = O ( 1 6 n 3 ) = O ( n 3 ) \sum_{i=1}^{n}\sum_{j=1}^{i}\sum_{k=1}^{j}1=\sum_{i=1}^{n}\sum_{j=1}^{i}j=\sum_{i=1}^{n}\frac{i(i+1)}{2}=O(\frac16n^3)=O(n^3) i=1∑nj=1∑ik=1∑j1=i=1∑nj=1∑ij=i=1∑n2i(i+1)=O(61n3)=O(n3)
两长度分别为m和n的升序链表合成为一个长度为m+n的降序链表,最坏情况下时间复杂度为: O ( m a x ( m , n ) ) O(max(m,n)) O(max(m,n))
【说明】:考查加法规则
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)))
int func(int n){
int i = 0, sum = 0;
while(sum < n) sum += ++i;
return i;
}
时间复杂度为 n \sqrt{n} n
对于给定输入规模n 要找出循环次数t和n的函数关系 f ( t ) < n f(t)<n f(t)<n,随后反解
本题中循环次数t满足 ( 1 + t ) ∗ t 2 < n \frac{(1+t)*t}{2}<n 2(1+t)∗t<n,在n足够大时影响左侧的是二阶的 t 2 t^2 t2, t 2 < n t^2 < n t2<n,故循环次数即时间复杂度为 n \sqrt n n
总结
- 把握好循环次数t,找准t和输入问题规模n的关系。
- 递归程序一般使用公式递推,格式如下:
T ( n ) = 1 + T ( n − 1 ) = 1 + 1 + T ( n − 2 ) = . . . = n − 1 + T ( 1 ) T(n)=1+T(n-1)=1+1+T(n-2)=...=n-1+T(1) T(n)=1+T(n−1)=1+1+T(n−2)=...=n−1+T(1)
主要就是一直化简到 T ( 1 ) T(1) T(1)常量为止。其他的常用的方法还有递归树和主方法