为什么需要复杂度分析
数据结构与算法的目标就是让计算机执行代码的时间和所用的空间尽量减少。为了达成这个目标,我们需要从时间和空间两个维度去分析代码,进而引申出渐进时间复杂度跟渐进空间复杂度,两者分别简称为时间复杂度和空间复杂度。时间复杂度和空间复杂度并不是真正代表着代码运行所用时间和空间,而是代码运行的时间跟所需空间所数量规模增大而变化的趋势。两者的总公式就是T(n)=O(f(n))*,这个f(n)=代表不同函数。
常用时间复杂度级别
时间复杂度粗略分两种,多项式量级跟非多项式量级(多个字母相乘除或者单字母单数字的为单项式,多个单项式相加或相减为多项式),其中非多项式只有O(2n)(2的n次方) 和 O(n!)。
计算时间复杂度之前先要记住3个规则:
1.只关注循环执行次数最多的一段代码
##函数声明不算,从第二行算,第二第三行复杂度都是1,第四行方法声明不算,第五行在循环执行n次,
##第五行代码复杂度为n,所以这段代码复杂度是=1+1+n,常数不要,取最多的n,所以这段代码复杂度为o(n)
int cal(int n) {
int sum = 0;
int i = 1;
for (; i <= n; ++i) {
sum = sum + i;
}
return sum;
}
2.如果有0.3n,2n这种,要去除前面的系数,时间复杂度为o(n)
3.常量级的时间复杂度都是O(1),什么是常量级请看下面内容
常用的时间复杂度有:
- O(1)常量
##忽略声明,不管代码有多少行上千行上万行,只有没有for,while循环(n不能固定)及递归语句,这些代码的复杂度都算O(1)(一个语句复杂度算1,所以这里复杂度是2,但是常数复杂度不管是2还是10000,都算O(1))
void test(int n){
system.out.println("hello");
system.out.println("hello");
}
- O(logn)和O(nlogn)对数
##第0次循环,i=1,第1次循环结束后变成2,之后循环一次乘2,所以i的值是1,2,,4,,8,16...即是2的0次方,
##2的1次方,2的3次方,所以x次循环后2的x次方=n,这个x就是时间复杂度,x=logn
##O(logn)
void test(int n, int 1= 1){
while(i<=n){
i = i*2;
}
}
##O(3logn)
void test(int n,int i =1){
while(i<=n){
i = i*2;
system.out.println("hello");
system.out.println("hello");
}
}
- O(n)线性
##for循环执行两次,所以n个循环后复杂度为2n,不管2n,3n还是n/3都算O(n)
void test(int n){
for(int 1= 1;i<=n;i++){
system.out.println("hello");
system.out.println("hello");
}
}
- O(n2)平方
##cal第一次循环,复杂度是1*1,第二次是2*2,所以cal的复杂度是n*n,f的复杂度是f,但是只取最高项n的平方
int cal(int n) {
int ret = 0;
int i = 1;
for (; i < n; ++i) {
ret = ret + f(i);
}
}
int f(int n) {
int sum = 0;
int i = 1;
for (; i < n; ++i) {
sum = sum + i;
}
return sum;
}
- O(m+n)
##因为n和m都是不可估计的,所以都不能省略,所以此时时间复杂度是m+n
int cal(int m, int n) {
int sum_1 = 0;
int i = 1;
for (; i < m; ++i) {
sum_1 = sum_1 + i;
}
int sum_2 = 0;
int j = 1;
for (; j < n; ++j) {
sum_2 = sum_2 + j;
}
return sum_1 + sum_2;
}
空间复杂度
空间复杂度就是在储存数据这方面下手,,要用多大空间去装,去进行一个分析。
空间复杂度的常见类别
1.常量空间O(1)
##空间大小固定,就给你一个或者多个单元,都是固定好的,跟时间复杂度一样都算是O(1)
void (int n){
int a =3;
int b =1;
}
2.线性空间(储存数据结构为线性的,例如数组等线性结构),O(n)
void (int n){
int[] array = new int[n];
}
3.二维空间(二维数组),O(n*n)
void (int n){
int[][] array = new int[n][n];
}
3.递归空间(栈)O(n)
##递归所涉及的结构是栈,n一直-1,所以复杂度是o(n)
void (int n){
if(n <1){
return}
f(n-1)
}
特别:平均时间复杂度,最坏复杂度,最好复杂度
// n表示数组array的长度
int find(int[] array, int n, int x) {
int i = 0;
int pos = -1;
for (; i < n; ++i) {
if (array[i] == x) {
pos = i;
break;
}
}
return pos;
}
看上面代码,查询代码,当x在数组首位,此时复杂度为O(1),此时便为最好复杂度,即在最理想的情况下
故,最坏复杂度就是在最坏情况下,即x在数组尾位的复杂度O(N)
平均复杂度又称期望值,学过高中数学的可知期望值等于权值概率,在上述代码中,x有两种可能,要么在数组中,要么不在数组中,所以x的期望值算法如下(图来自王争老师的《数据结构与算法》,前半段x为在数组情况下,最后一个n1/2为x不在数组中):
时间复杂度和空间复杂度取舍
鱼和熊掌不可兼得,不同情况下根据不同需求牺牲时间或者空间,一般情况下,牺牲空间换取时间比较多