基本概念
一. 什么是数据结构
解决问题方法的效率跟数据的组织方式有关
图书馆放书:(考虑插入和查找的难易)
- 随便放:
- 有空位就插入,易插;
- 需要遍历所有的图书,难找
- 按照拼音字母顺序排放(插入时,所有插入之后的数据均需要后移一位,浪费;查找时,二分查找);
- 二分查找确定位置,移出空位,但是当需要移动的量太多,会造成浪费;
- 二分查找
- 按区域划分,区域内按照字母顺序排放(插入时,先分类别,二分查找确定位置,移出空位(移动数量和2相比少了很多);
- 先分类别,二分查找确定位置,移出空位,与2相比,需要移动量会少
- 先分类别,再二分查找
解决问题方法的效率跟空间的利用效率有关
void PrintN101_1(int N) { for (int i = 1; i <=N; i++) { printf("%d\n", i); } }
void PrintN101_2(int N) { if (N) { PrintN101_2(N - 1); printf("%d\n", N); } }
第二种递归输出当N特别大时,会导致存储空间不够,溢出,递归函数吃掉所有空间,爆掉了
解决问题方法的效率跟算法的巧妙程度有关
多项式:f(x)=a0+a1x+...+an-1xn-1+anxn;时间复杂度为T(n)=C1n2+C2n
double f103_1(int n, double a[], double x) { int i; double p = a[0]; for (i = 1; i <= n; i++) p += (a[i] * pow(x, i)); return p; }
提取上个多项式的公因式:f(x)=a0+x(a1+x(...(an-1+xan))...)); 时间复杂度为T(n)=C*n
double f103_2(int n, double a[], double x) { int i; double p = a[0]; for (i = n; i > n; i--) p = a[i - 1] + x * p; return p; }
第二种时间复杂度上面远远优于第一种方法
抽象数据类型
只描述数据对象集和相关操作集“是什么”,并不涉及“如何做到”的问题
clock()函数的返回类型为clock_t
注:函数运行时间太短,不到一个clock_t的时候,可以重复执行相同的函数,后进行平均
二. 什么是算法
算法描述不依赖于任何一种计算机语言以及具体实现的手段,不关心具体描述细节(伪代码)
空间复杂度:占用存储空间大小。PrintN101_2 函数中递归函数复杂度为C*N,由于每次递归都会占用一个新的内存空间,N过大内存不够用时会爆掉
时间复杂度:乘除耗时远大于加减
最坏复杂度:(最关心)Tworst(n)
平均复杂度: Tavg(n)
Tavg(n) < Tworst(n)
复杂度渐进表示法
log n<n<nlog n<n2<2n<n!
复杂度分析小窍门:
- 若两段算法的复杂度相加,取较大的那一个;两端算法嵌套,则复杂度相乘;
- n的k阶多项式的复杂度取最高阶的复杂度
- for循环的复杂度等于循环次数乘以循环体代码的复杂度
- if-else复杂度取决与条件判断复杂度和两个分支的复杂度,总体复杂度取其中最大的
三. 案例
给定N个整数的序列{A1,A2,...,AN},求子序列的最大和的序列
算法一:循环遍历求出每个子序列的和
int MaxSubseqSum130_1(int A[], int N) { int ThisSum, MaxSum = 0; for (int i = 0; i < N; i++) { for (int j = i; j < N; j++) { ThisSum = 0; for (int k = i; k < j ; k++) { ThisSum += A[k]; } if (ThisSum > MaxSum) MaxSum = ThisSum; } } return MaxSum; }
此时的时间复杂度为T(N)=O(N3)
算法二:算法一的第三层循环可以舍去,因为随着 j 的每次增加与上一个子序列的差值就只差增加的那一项,所以可以在之前的基础上直接加上当前项
int MaxSubseqSum130_2(int A[], int N) { int ThisSum, MaxSum = 0; for (int i = 0; i < N; i++) { ThisSum = 0; for (int j = i; j < N; j++) { ThisSum += A[j]; //随着 j 的每次增加与上一个子序列的差值就只差增加的那一项 if (ThisSum > MaxSum) MaxSum = ThisSum; } } return MaxSum; }
此时算法的复杂度为T(N)=O(N2)。每当这时,考虑能不能把时间复杂度再往下降。
算法三. 分而治之:不断二等分递归
先不断二分求出其中每一部分的最大子序列之和,并且求出分割处最大子序列之和,三者比较保留最大的那个数
此时算法的复杂度为T(N)=O(NlogN)
算法四. 在线处理:即时处理
int MaxSubseqSum130_4(int A[], int N) { int ThisSum=0, MaxSum = 0; for (int i = 0; i < N; i++) { ThisSum += A[i]; if (ThisSum > MaxSum) MaxSum = ThisSum; else if (ThisSum < 0) //如果当前子列和为负数 ThisSum = 0; //则不可能使后面的部分和增大,抛弃之 } return MaxSum; }
此时的时间复杂度:T(N)=O(N)
综上所述,算法四的时间复杂度最低