算法效率
时间复杂度
时间开销与问题规模n之间的关系称为时间复杂度
1、如何评估算法时间开销
让算法先运行,事后统计运行时间
事后统计存在的问题
注:以下是与算法本身无关的外界因素,无法排除
一:和机器性能有关,如:超级计算机 v.s. 单片机
二:和编程语言有关,越高级的语言执行效率越低
三:和编译程序产生的机器指令质量有关
无法事先估计
有些算法是不能事后再统计的,例如:导弹控制算法
算法时间复杂度:应采用事前预估算法时间开销T(n) 与问题规模n的关系(T表示time)
2、算法一:单循环型
public void printHelloWorld (int n) {
1:int i = 1;
2:while (i < n) {
3: i++;
4: System.out.println("Hello World");
}
5:System.out.println("Hello World more then" + n);
}
语句频度为(设n为3000)
第一行-------1次
第二行-------3001次
第三与第四行-------3000次
第五行-------1次
T(3000) = 1 + 3001 + 2 * 3000 + 1
则时间开销与问题规模n的关系为T(n) = 3n + 3
时间复杂度的全称为渐进时间复杂度(渐进是因为n是要趋向于无穷大的时候才能统计出时间复杂度)
对以上算法现象,思考提出两个问题
问题一:是否可以忽略表达式某些部分?
答:可以只考虑阶数,并使用大O记法表示
问题二:如果有好几千行代码怎么办,要按这种方法一行行计算?
答:只需要考虑最深层循环次数与n的关系
举例:
T(n) | n = 3000 | n = 1,000,000 |
---|---|---|
3n + 3 | 9003 | |
3n | 9000 | |
9999n | 29,997,000 | 9,999,000,000 |
n² + 3n + 1000 | 9,010,000 | |
n² | 9,000,000 | 1,000,000,000,000 |
n³ + n² + 9,999,999 | 27,018,999,999 | |
n³ | 27,000,000,000 |
结论一:可以只考虑阶数高的部分
结论二:问题规模足够大时,常数项系数也可以忽略
a)加法规则:多项相加,只保留最高阶的项,且系数变为1
T(n) = T1(n) + T2(n) = O(f(n)) + O(g(n)) = O(max(f(n),g(n)))
b)乘法规则
T(n) = T1(n) * T2(n) = O(f(n)) * O(g(n)) = O(f(n)*g(n))
3、时间复杂度排序
O(1) < O(㏒2 n) < O(n) < O(n㏒2 n) < O(n²) < O(n³) < O(2 ^ n) < O(n!) < O(n ^ n)
算法时间复杂度排序来源
O(n)与O(㏒2 n)
O(n²)与O(2 ^ n)
T(3000) = 1 + 3001 + 2 * 3000 + 1时间开销与问题规模n的关系为T(n) = 3n + 3,则T(n) = 3n + 3 = O(n)
结论一:顺序执行的代码只会影响常数项,可以忽略
结论二:只需挑循环中的一个基本操作分析它的执行次数与n的关系即可计算时间复杂度
4、算法二:嵌套循环型
public void printHelloWorld (int n) {
int i = 1;
while (i < n) {
i++;
System.out.println("Hello World");
for(int j = 1; j < n; j++) {
System.out.println("Hello World1");
}
}
System.out.println("Hello World more then" + n);
}
时间复杂度:T(n) = O(n) + O(n²) = O(n²)
5、算法三:指数递增型
public void printHelloWorld (int n) {
int i = 1;
while (i < n) {
i = i * 2;
System.out.println("Hello World");
}
System.out.println("Hello World more then" + n);
}
时间复杂度:
设最深层循环的语句频度(总共循环的次数)为x,则由循环条件可知,循环结束时刚好满足
2 ^ x > n
x = ㏒2 n + 1
T(n) = O(x) = O(㏒2 n)
6、算法四:搜索数字型
设arr数组中乱序存放了1~n即int[] arr = {1,2,…,n-1,n}
public void searchElement (int[] arr,int n) {
for(int i = 0; i < n; i++) {
if(arr[i] == n) {
System.out.println("Hello World" + i);
break;
}
}
}
计算搜索算法时间复杂度
算法时间复杂度情况 | 描述 | 时间复杂度 |
---|---|---|
最好情况 | 最好情况下的算法的时间复杂度,此时为:元素n在第一个位置 | T(n) = O(1) |
最坏情况 | 最坏情况下的算法的时间复杂度,此时为:元素n在最后一个位置 | T(n) = O(n) |
平均情况 | 所有输入示例等概率出现的情况下,算法的期望运行时间,此时为:假设元素n在任何一个位置的概率相同为1/n | T(n) = O(n) |
平均情况:循环次数x = (1 + 2 + 3 +…+ n) * 1 / n = (1 + n) / 2
7、常见排序算法时间复杂度
注:稳定性的判断是由排序中的两个相等的数,在排序完成前的前后顺序与排序完成后的前后顺序是否改变,如果改变即为不稳定,不改变即为稳定
空间复杂度
空间开销(内存开销)与问题规模n之间的关系称为空间复杂度
1、原地工作—算法所需内存空间为常量
无论问题规模如何变化,算法运行所需的内存空间都是固定的常量,算法的空间复杂度为S(n) = O(1)
注:S表示Space
2、算法一
public void test (int n) {
int arr[n];
int i;
// 省略代码。。。
}
一个int变量占4字节
则所需内存空间为= 4 + 4n + 4 = 4n + 8
即S(n) = O(n)
3、算法二
public void test (int n) {
int arr[n][n];
int i;
// 省略代码。。。
}
即S(n) = O(n²)
4、算法三
public void test (int n) {
int arr[n][n];
int other[n];
int i;
// 省略代码。。。
}
即S(n) = O(n²) + O(n) + O(1) = O(n²)