时间复杂度 & 空间复杂度
一.时间复杂度
一.简介
- 定义:算法中某个函数有n次基本操作重复执行,用
T(n)
表示,现在有某个辅助函数f(n)
,使得当n趋近于无穷大时,T(n)/f(n)
的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n))
,称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度- T(n):循环次数(操作次数)
- f(n):量级函数:
[ 1,n,log2n,nlog2n,n2,n3,n^4 ]
- O(fn):渐进时间复杂度:当n趋于无穷大时,T(n)/f(n)是常数的时候的f(n)。把fn放到O()里
- 常见时间复杂度
- 分类
- 常数阶O(1)
- 对数阶O(logN)
- 线性阶O(n)
- 线性对数阶O(nlogN)
- 平方阶O(n²)
- 立方阶O(n³)
- K次方阶O(n^k)
- 指数阶(2^n)
- 排序:
O(1) < O(logn) < (n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
- 分类
- 总结:时间复杂度是总运算次数表达式中受n的变化影响最大的那一项(不含系数)
二.代码示例
- 常数阶O(1)
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
无论代码执行了多少行,只要是没有循环等复杂结构,那这个代码的时间复杂度就都是O(1)
- 线性阶O(n)
for(i=1; i<=n; ++i)
{
j = i;
j++;
}
单层for循环中。for循环n遍,则时间复杂度就是O(n)
- 对数阶O(logN)
int i = 1;
while(i<n)
{
i = i * 2;
}
在循环中对循环条件进行乘法操作的一般都是对数阶
循环里面,每次都将 i 乘以 2,乘完之后,i 距离 n 就越来越近了。
我们试着求解一下,假设循环x次之后,i 就大于 2 了,此时这个循环就退出了,也就是说 2 的 x 次方等于 n,那么 x = log2^n
也就是说当循环 log2^n 次以后,这个代码就结束了。
因此这个代码的时间复杂度为:O(logn)
- 线性对数阶O(nlogN)
for(m=1; m<n; m++)
{
i = 1;
while(i<n)
{
i = i * 2;
}
}
将时间复杂度为O(logn)的代码循环N遍的话,那么它的时间复杂度就是 n * O(logN),也就是了O(nlogN)。
- 平方阶O(n²)
for(x=1; i<=n; x++)
{
for(i=1; i<=n; i++)
{
j = i;
j++;
}
}
双层for循环,且循环条件都为n的,则时间复杂度就是 O(n * n),即 O(n²)
- 指数阶
long aFunc(int n) {
if (n <= 1) {
return 1;
} else {
return aFunc(n - 1) + aFunc(n - 2);
}}
显然运行次数,T(0) = T(1) = 1,同时 T(n) = T(n - 1) + T(n - 2) + 1,这里的 1 是其中的加法算一次执行。
显然 T(n) = T(n - 1) + T(n - 2) 是一个斐波那契数列。
通过归纳证明法可以证明,当 n >= 1 时 T(n) < (5/3)^n,同时当 n > 4 时 T(n) >= (3/2)^n。
所以该方法的时间复杂度可以表示为 O((5/3)^n),简化后为 O(2^n)。
二.空间复杂度
一.代码示例
- 空间复杂度 O(1)
int i = 1;
int j = 2;
++i;
j++;
int m = i + j;
如果算法执行所需要的临时空间不随着某个变量n的大小而变化,即此算法空间复杂度为一个常量,可表示为 O(1)
- 空间复杂度 O(n)
int[] m = new int[n]
for(i=1; i<=n; ++i)
{
j = i;
j++;
}
这段代码中,第一行new了一个数组出来,这个数据占用的大小为n,这段代码的2-6行,虽然有循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可,即 S(n) = O(n)