程序的时间复杂度与空间复杂度该怎么算?

时间复杂度

为什么要有时间复杂度?

第一、在我们的测试环境里面,不同的硬件跑出来的结果是不一样的,比如,i7与i3的机器跑同样的代码花的时间就不一样。

第二、用不同的数据去测试,那么花的时间也是不一样的。

通用公式

所以我们需要一套公式去计算复杂度,通用公式:T = O(N),T表示代码执行的时间,N表示每行代码执行的次数总和,O表示所有代码的执行时间T与每行代码的执行次数总和成正比。(假设每行代码执行的时间是一样的)

O表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度(asymptotic time complexity),简称时间复杂度。

开始运用方法:

方法一. 只关注循环执行次数最多的一段代码

int cal(int n) { 
     int sum = 0; 
     int i = 1; 
     for (; i <= n; ++i) { 
        sum = sum + i;
     }
     return sum;
 }

其中2、3行是常量级别的代码与n的大小无关,所有不用管,执行次数最多的事第4、5行代码,所以我们只管这个两行,时间复杂度是O(n).

方法二、乘法法则,嵌套代码的复杂度等于内外嵌套复杂度的乘积

int cal(int n) {
	int ret = 0;
	int i = 1;
	for (; i < n; ++i) { //第一段 这个函数的复杂度是O(n)
		ret = ret + f(i);
	}
}
int f(int n) {
	int sum = 0;
	int i = 1;
	for (; i < n; ++i) {//第段段 这函数的复杂度是O(n),合起来就是O(n^2)
		sum = sum + i;
	}
	return sum;
}

这里单看第一段函数的复杂度是O(n),单看第二段函数的复杂度也是O(n),有因为嵌套了,合在一起就是O(n^2)了。

方法三、计算复杂度是总复杂度最多的那段

int cal(int n) {
	int sum_1 = 0;
	int p = 1;
	for (; p < 100; ++p) {//第一段为O(1)
		sum_1 = sum_1 + p;
	}
	int sum_2 = 0;
	int q = 1;
	for (; q < n; ++q) {//第二段为O(n)
		sum_2 = sum_2 + q;
	}
	int sum_3 = 0;
	int i = 1;
	int j = 1;
	for (; i <= n; ++i) {//第三段为O(n^2)
		j = 1;
		for (; j <= n; ++j) { //这里又套了一层循环
			sum_3 = sum_3 +  i * j;
		}
	}
	return sum_1 + sum_2 + sum_3;
}

第一段的执行的次数是常量,所以是O(1),第二段执行了n次所以是O(n),第三段执行了O(n2),所以这段代码最终的时间复杂度为O(n^2)

再看看常见的多项式时间复杂度

第一种:O(1)

int i = 8;
int j = 6;
int sum = i + j;

这种情况,主要没有递归、循环,即使有上千万行的代码,时间复杂度也是O(1)

第二种: O(logn)、O(nlogn)

 i=1;
 while (i <= n)  {
   i = i * 2;
 }

由上可知道,它的代码执行轨迹就是:2^1 * 2^2 * 2^3 * 2^4 …… 2^x = n 为止,那么求解这个X,就是以2为底的log函数:x=log2n,所以这段代码的时间复杂度是O(log2n),如果在外面再套一层循环m,那么时间复杂度就是O(m*log2n)

第三种:O(m+n)、O(m*n)


int cal(int m, int n) {
  int sum_1 = 0;
  int i = 1;
  for (; i < m; ++i) {//第一段复杂度为O(n)
    sum_1 = sum_1 + i;
  }

  int sum_2 = 0;
  int j = 1;
  for (; j < n; ++j) {//第二段复杂度为O(m)
    sum_2 = sum_2 + j;
  }

  return sum_1 + sum_2;
}

第一段和第二段的复杂分别是O(n)和O(m),属于同一级别,都是n级别(大于1,小于N的平方),所以cal()方法的复杂度是O(m+n),如果第一段和第二段有嵌套的话,那么可以相乘:O(m*n)

空间复杂度分析

空间复杂度就很简单了,就是一句“占用有多少个变量”就可以解释完毕!


void print(int n) {
  int i = 0;
  int[] a = new int[n]; // 只有这里占用了n个空间所以空间复杂度是O(n)
  for (i; i <n; ++i) {
    a[i] = i * i;
  }

}

从上面可以看出,只有第3行 new的时候占用了n个空间,其它行占用的空间是O(1),所以答案是O(n)

我们平时能用的空间复杂度一般是是O(1),O(n),O(n^2),其他的O(logn)、O(nlogn)都是用不到的

继续拓展

=========分割线==========

复杂的时间复杂度该怎么算?有下面的问题:


// 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,那就不需要继续遍历剩下的 n-1 个数据了,那时间复杂度就是 O(1)。但如果数组中不存在变量 x,那我们就需要把整个数组都遍历一遍,时间复杂度就成了 O(n)。所以,不同的情况下,这段代码的时间复杂度是不一样的。

这个需要用到概率论来解决,想继续钻研的朋友可以扫码下面的二维码,听听专业老师的讲解。

数据结构与算法之美
数据与算法之美

  • 11
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值