算法梳理之时间空间复杂度

算法优劣是有专门的标准的,这些其实大学中都有学过,也不是很难,就是时间复杂度和空间复杂度,来精确描述出一个算法在耗费时间和耗费空间上的优劣

一.时间复杂度
  • 时间复杂度的计算其实不是真正算法执行的时候所用的时间,因为我们在不同的环境中执行相同的代码,因为硬件的不同导致我们运行的环境差异,从而导致同样的计算方法可能得出不同的结论,更往底层追究的话,其实就算完全一样的硬件也不会每次都完完全全一样的时间,所以我们时间复杂度的计算其实是从代码执行的次数来看的,这样就能更公平的看出算法的优劣了
    不管怎么说,我们还是要有一详细表述算法时间复杂度的方法的,这我们就要用到一个通用方法,叫大O符号表示法,这个表示法其实本质上来讲叫渐进时间复杂度,我们也都用这个来表示时间复杂度

我们常用的时间复杂度有以下七种,算法时间复杂度依次增加

  • O(1)常数型
  • O(log2 n)对数型
  • O(n)线性型
  • O(n log2 n)二维型
  • O(n^2)平方型
  • O(n^3)立方型
  • O(2^n)指数型

我们来看看这个大O符号表示法是怎么使用的

1.O(1)常数型
let a = 1;
let b = 2;
b = a + b;
a = a + b;
a = a * b;

从这个开始说起,这个就是时间复杂度最低的情况,为什么,因为我们的代码一句只执行依次一次,那么总体代码的执行次数就是1,那我们的时间复杂度就表示为O(1),有的人说了,这不是一共执行了五行代码吗,不应该是O(5)吗,其实不是的,这常数执行次数的我们都表示为O(1)

2.O(log2 n)对数型
for(let i = 1; i < n; i++){
	......
	i = i * 2;
}

我们上边说了,要看代码的执行次数,这就开始说,次数该怎么计算,我们一共有几行代码,除去中间的忽略掉的执行逻辑我们有两行逻辑代码,但是两行的逻辑代码执行次数是关联的是一样的,所以我们算复杂度一起算就可以了,这段for循环逻辑执行了多少次,当i每次都*2的时候,如果n是16,我们每次*2需要多少次就能达到16,四次,我们应该都学习过这类数学题把,反向来看,不就是log2 n嘛,因为2的log2 n次方等于n,那时间复杂度自然就是O(log2 n)了

3.O(n)线性型
for(let i = 1; i < n; i++){
	......
	i ++;
}

这个更好理解,随着n的变化,次数将会进行什么样的变化,将会跟随n来变化,1*n那时间复杂度毋庸置疑就是n了嘛,O(n)

4.O(nlog2n)二维型
for(let j = 1; j < n; i++){
	for(let i = 1; i < n; i++){
		......
		i = i * 2;
	}
}

这个也很好理解,例子应该写的很清楚,双层循环,外层循环时n次,内层循环是log2n次,总体的循环就是n*log2n次了,那就是O(n log2 n)

5.O(n^2)平方型
for(let j = 1; j < n; i++){
	for(let i = 1; i < n; i++){
		......
		i++;
	}
}

什么叫一通百通,这个也就是for循环的嵌套,但是内部执行n次外部执行n次,这样算下来就是n的平方次了,那复杂度就是O(n^2)

6.O(n^3)立方型

这个就不多说了,三层嵌套,总次数就是nnn,也就是n的三次方就是O(n^3)

7.O(2^n)指数型
function fun(n){
	if(n > 0){
	 	return n
	 }else{
	 	return fun(--n) + fun(--n)
	 }
}

这个就相对来说复杂了一些,因为有了递归,只有递归才能让n影响到代码这么大的执行次数,因为有一次不满足,就会函数被调用两次,如果函数中–n还是不满足,那第二次的时候两个被调用的函数就会分出去四个,这样越分越多,每次都*2,自然也就成为了2^n,
2的n次方,其实如果我们分的时候是分了三个那就是3的n次方了,所以这就是O(2^n)

二.空间复杂度

相对来讲空间复杂度和时间复杂度是一样的计算方法,不过不再是计算代码的执行次数,而是记录空间或者说临时空间被占用的多少,那其实我们空间复杂度其实比时间复杂度更好计算
空间复杂度其实常见的少一些,大概也就是

  • O(1)
  • O(n)
  • O(n^2)
1. O(1)
let a = 1;
let b = 2;

这样来看我们的空间复杂度是多少,其实就是常数空间复杂度,因为在整个流程下来与n无关,当代码中没有循环创建变量的时候,就是尝试的空间复杂度了,也就是O(1)

2.O(n)
let a = 1;
let b = 2;
let c = [];
for(let j = 1; j < n; i++){
 c.push(j)
}

这样我们再来看,这个空间复杂度是多少,明显是n,因为我们在一个循环中反复创建了一个数组中n个空间,也就是O(n)

3.O(n^2)
let c = {}
for(let j = 1; j < n; i++){
	c[j]=[]
	for(let i = 1; i < n; i++){
		c[j].push(i)
	}
}

这个的空间复杂度我们计算一下一共生成了多少个数组,n个数组,每个数组中储存了多少数据,n个,那也就是随着n的改变,我们储存的数据会变成n^2,也就是 O(n^2)

总结:时间复杂度我们有时候会特别的关注,为什么呢,只要不是极其愚蠢的空间储存,在硬件的加持下,我们都会有一个比较良好的储存环境,不用担心空间储存过多导致的问题,但是我们的时间复杂度实际上一定程度上反应了我们不同算法下的处理速度,我们在当前编程背景下,良好的性能和反应速度才是我们最关心的,当然空间复杂度一定也是不能忽略的,我们所造成的内存泄漏其实有一定情况都是不考虑空间复杂度,在for循环内反复定义变量,储存不需要的垃圾信息,都是不好的,程序是否简洁是否足够优秀,其实不光是时间复杂度的事情,跟空间复杂度一定也是息息相关的

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值