常见的复杂度分析
No.1 什么是数据结构和算法?
数据结构就是一组数的存储结构,而算法是怎么将这组数进行存储,是进行更深层次的优化存储。.................
数据结构和算法的作用,我们平时写的代码不够完美,可能占用内存较高,运行消耗时间也很长。
No.2 复杂度分析
数据结构和算法是解决代码的“快”和“省”的问题,如何让代码运行的更快,更能节省程序运行时占用的内存空间等,所以代码的执行效率是一个非常重要的指标。
复杂度分析相比于性能测试,复杂度不依赖执行环境,成本较低,效率高。
No.3 大O复杂度表示法
复杂度分为时间复杂度和空间复杂度,时间复杂度代表了数据量的递增,代码执行后的时间也会随之递增的,用timer表示代码执行一次的使用时间,注意这里并不能正真意义上去代表代码执行一次使用的时间,而是表示着下面可以观察这组代码来分析时间复杂度。
public class Demo1{
public int sum(int n){
int sum = 0; // 1次
for(int i=0;i<=n;i++){ // i=0一次,i<=n n次 ,i++ n次
sum = sum+i; // n次
}
return sum; // 一次
}
}
分析:上面整个代码从开始执行到结束后,看方法体内部的代码,循环外为代码执行了两次,内部循环体总共执行了3n+1次,所以整个代码的时间复杂度为timer*(3n+3)。时间复杂度只描述了对数据量n的处理,当数据量n逐渐增大时,处理的时间也会随之增大的一个变化趋势,这就是时间复杂度的分析。
注:当数据量n变得很大时,公式中的低阶,常量,系数三部分并不能左右其增长趋势,反之,影响变化趋势的却是公式中的高阶,当数据量足够大的时候,可以忽略公式中的低阶数据了。
一般的,可以将时间复杂度从简表示,例如上面的时间复杂度可以表示为O(n),最大系数没有高阶次。
No.4 复杂度的分析方法
最大循环原则:大O复杂度法只表示一种变化趋势,公式中的低阶,常量,系数三个部分都不会随着n的增长而影响整个数据的变化趋势,我们只需要记录一个最大的量级就可以了,因此分析一个算法或者一个代码执行的次数最大的那一段代码即可。
加法原则:当一段代码中出现多个循环,或者其他的代码相加起来的情况时,我们只需要考虑用系数最大的那个时间复杂度,其他系数比较小的可以不用加到里面来。例如:
public int sum(int n){
int sum1 = 0;
int p = 0;
for(;p<=100;p++){
sum1=sum1+p;
}
int sum2 = 0;
int q =1 ;
for(;q<n;q++){
sum2 = sum2+q;
}
int sum3 = 0;
int i = 1;
int j = 1;
for(;i<=n;i++){
j=1;
for(;j<=n;j++){
sum3 = sum3+i*j;
}
}
return sum1+sum2+sum3;
}
分析后这段代码的时间复杂度是O(n^2)!
乘法原则:在一段代码中有循环,循环中还能调用其他的方法的时候,这个时候就可以使用乘法原则来判断时间复杂度了。
public int sum(int n ){
int ret = 0;
int i = 1;
for(;i<n;i++){
ret = ret+func(i);
}
return ret;
}
public int func(int n){
int sum = 0;
int i = 1;
for(;i<n;i++){
sum = sum + i;
}
return sum;
}
注:方法中有方法调用,第一个复杂度是O(n),第二个也是O(n),用乘法思想就是O(n^2)
No.5 常见的复杂度
常数 O(1), 线性 O(n), 对数 O(log n), 线性对数 O(n*log n), 等。
No.6 几种复杂度的分析
对数:o(log n)
public void Demo1(int n){
int i = 1;
while(i<=n){
i=i*2;
}
}
分析:循环内是进行2倍数的扩大化,假设执行到第k次后达到n了,次复杂度就可以表示为 O(log n),也就是对数运算方法。那对于O(n*log n)呢? 我们可以分析,如下面的代码:
public void Demo2(int n ){
int i = 1;
for(;i<=n;i++){
i=i+Demo3(n);
}
}
public void Demo3(int n){
int i = 1;
while(i<=n){
i=i*2;
}
}
此方法中使用了方法调用,一个方法里面调用了另一个方法,对第一段代码来说,复杂度为O(n),对第二段代码来说,复杂度是O(log n),所以综合起来复杂度就是O(n*log n).
No.7 空间复杂度
时间复杂度表示了代码执行时间随着数据N增长的一个变化趋势,空间复杂度表示了代码执行所需要的额外空间和数据n增长的一个变化趋势,描述了他们之间的一个增长关系。
public void Demo5(int n){
int i = 1;
int sum = 0;
for(;i<=n;i++){
sum=sum+i;
}
}
public void Demo6(int n){
int i = 0;
int [] a = new int [n];
for(;i<n;i++){
a[i]=i*i;
}
for(i=n-1;i>=0;i--){
System.out.println(a[i]);
}
}
分析:这两段代码都带有循环,第一,数据n的代入并不影响代码运行时所需要的空间,是一个常数级的空间复杂度,n的增长并不印象内存空间的开辟。第二段代码,很明显代码中定义了一个数组,而且数组初始化长度是根据数据n的大小而决定的,当n增大的时候,代码运行的空间复杂度也随之增大,这是一个相对的关系,所以Demo6的代码空间复杂度是随着n的增大而增大的,这是一个O(n)的空间复杂度。
有不对的地方还希望大家共同指正!