时间复杂度
首先时间复杂度常有一下几种表示:
- 常数阶O(1)
- 对数阶O(logN)
- 线性阶O(n)
- 线性对数阶O(nlogN)
- 平方阶O(n²)
- 立方阶O(n³)
- K次方阶O(n^k)
- 指数阶(2^n)
O(1) < O(log2n) < O(n) < O(nlog2n) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)
从上到下复杂度越高且执行效力越低。
下面是一些比较常见的一些用法:
1.常数阶O(1)
时间复杂度呢,理解起来不难,常数阶O(1)其实就是在代码运行当中没有循环,不管这段代码有多长,它都是常数阶。
例如
int a=0;
int b=0;
int c=0;
a++;
b++;
c++;
这段代码一直往下写只要没有循环,多长都是O(1)。
2.线性阶O(n)
上面说O(1)是没有循环,那么线性阶就是含有一个循环,在循环里循环n次,叫做O(n)
例如:用算法表白——“爱你n遍”
void love(int n) //n为问题规模
{
int i=1; //爱你的程度
while(i<=n)
{
i++; //每次爱你+1
printf("我爱你%d遍\n",i);
}
printf("爱你超过了%d遍\n",n);
}
//时间复杂度O(n)
计算语句频次:
3----1次
4-----3000次
5/6 ------3000次
9--------1次 5
T(3000)=1+3000+2*3000+1 =3*3000+2
时间开销与问题规模的n的关系:
T(n)= 3n+2
那么问题来了:如果有好几千行代码, 按这种方法需要一行一行数?
只需考虑最深层循环的循环 次数与 n 的关系。
T 1 (n)=3n+3
T 2 (n)=n^2+3n+1000
T 3 (n)=n^3+n^2+999999
可以只考虑阶数高的部分;问题规模足够大时, 常数项系数也可以忽略
多项相加,只保留最高阶的项,且系数变为1
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(f(n)× g(n))
3.平方阶O(n^2)
平方阶在O(n)里在嵌套一层循环。
例如:
void love(int n) //n为问题规模
{
int i=1; //爱你的程度
while(i<=n) //外层循环n次
{
i++; //每次爱你+1
printf("我爱你%d遍\n",i);
for(int j=1;j<=n;j++)
{
printf("我是钢铁侠\n"); //嵌套两层循环,循环n^2次
}
}
printf("爱你超过了%d遍\n",n);
} //O(n^2
时间开销与问题规模 n 的关系
T(n) = O(n) + O(n^2) = O(n^2)
顺序执行的代码只会 影响常数项,可以忽略
只需挑循环中的一个 基本操作分析它的执行次数 与 n 的关系即可
如果有多层嵌套循环, 只需关注最深层循环循环了几次
4.对数阶O(logn)
例如:
void love(int n) //n为问题规模
{
int i=1; //爱你的程度
while(i<=n)
{
i=i*2; //每次爱你翻倍
printf("我爱你%d遍\n",i);
}
printf("爱你超过了%d遍\n",n);
}
i:1 2 4 8 16 32 ..... =>2^(x-1)
x:1 2 3 4 5 6 .... x
2^(x-1) > n => x = log2n+1
计算上述算法的时间复杂度 T(n):
设最深层循环的语句频度(总共循环的次数)为 x,则 由循环条件可知,循环结束时刚好满足
2^(x-1) > n
x = log2n + 1
T(n) = O(x) = O(log2n)