算法
解决问题的办法
程序 = 数据结构 + 算法
算法的特性
- 输入(具有0个或多个输入)
- 输出((算法有1个或多个输出)
- 有穷性(能在有限的步骤内完成)
- 确定性(每一步骤都无歧义)
- 可行性(能在有限的时间内完成)
好算法的特征
- 正确性(能够正确的解决问题)
- 可读性(描述清晰)
- 健壮性(能够处理非法的输入)
高效率与低存储量需求
时间复杂度
我们先来看这样下面这段代码
#include <stdio.h>
void main(){
int i,input;
scanf("%d",&input);
for(i= 0;i<input;i++){
printf("%d\n",i);
}
}
打印变量i,我们输入100,就需要打印100次,输入1000,就需要打印1000次。如果输入n,那么就需要打印n次
记为:T(n) = n
n代表问题的规模
我们再来看下面这样一段代码
#include <stdio.h>
void main(){
int i,j,input;
scanf("%d",&input);
for(i=0;i<input;i++){
printf("%d\n",i);
}
for(j=0;j<input;j++){
printf("%d\n",j);
}
}
这段代码分别打印了变量i
和变量j
,程序一共执行了2n次
记为:T(n) = 2n
T(n) 就是时间复杂度
大O表示法
其实,我们有时候并不关心程序执行的基本步骤是多少步,我们更加关心地是增长趋势,n和2n是处于同一个数量级的
上面两个例子都可以表示为O(n) = n
O(n)即为大O表示法
常见的时间复杂度
O(1) < O(log2n) < O(n) < O(nlog2n) < O(n2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
最坏时间复杂度:最坏情况下算法的时间复杂度
平均时间复杂度:所有输入示例等概率出现的情况下,算法的期望运行时间
最好时间复杂度:最好情况下算法的时间复杂度
如判断某个值在不在数组中,如果刚好要找的值在数组中第一个元素,那么时间复杂度就是O(1),如果是最后一个元素,那么需要比较n次,时间复杂度就是O(n)。
我们一般考虑的是最坏时间复杂度,也就是在最坏情况下,程序需要执行多少步。
数组中每个元素的值都有可能是要查找的值,每个元素命中的概率为1/n,所以时间复杂度为
T(n) = (1 + 2 + 3 + ... + n) * 1/n = (1 + n) / 2
,用大O表示法记为O(n)
大O表示法,忽略常数项以及次要项
空间复杂度
占用内存的大小
我们来看下面这段代码
#include <stdio.h>
void main(){
int i,input;
scanf("%d",&input);
for(i=0;i<input;i++){
printf("%d\n",i);
}
}
我们定义了两个变量,i
和input
,一个整型变量占4个字节的内存,运行这段代码需要占用8个字节的内存,是一个常数,用大O表示法记为O(1)
我们再来看下面这段代码
#include <stdio.h>
#include <stdlib.h>
void main(){
int input,*p;
scanf("%d",&input);
p = (int*)malloc(input * sizeof(int)); // 申请一片连续的内存空间,p指向申请的内存空间首地址
p[0] = 1; // 赋值1 等价于 *p = 1;
p[1] = 2;
printf("%d",*p);
free(p); // 释放内存
}
我们输出10,会分配10 * 4
个字节的内存空间,输入100,会申请100 * 4
个内存空间。输入n次会申请S(n)= 4n
内存空间的大小。忽略常数项,大O表示法记为O(n)= n
n代表问题的规模
S(n) 即为空间复杂度
常见的空间复杂度
O(1) < O(log2n) < O(n) < O(nlog2n) < O(n2) < O(n^3) < O(2^n) < O(n!) < O(n^n)