算法笔记1--时间复杂度与空间复杂度

286 阅读4分钟

算法的时间复杂度与空间复杂度,都是基本的概念,这里花一点时间做个简单的介绍。

时间复杂度:通常表现为一个函数,来定性描述算法的运行时间。通过预估代码的基本操作执行次数来表现。

举例说明

1、 办公室有一桶水10L,2小时喝下去1L,需要20小时,如果是 n 升的水呢?那就是 n小时。如果用函数式来表达 T(n) = 2n;

2、办公室内一桶水 16L,每小时喝掉剩余的一半,那把水喝到只能1L,需要多久?就算方式是用16不断的除以2,用数学表达式就是以2为底16的对数 log216 。需要 4个小时,如果是n升水,则为 log n ,T(n) = log n;

...... 以上这种类比同样适用于对程序基本操作执行次数的统计。设 T(n) 为程序基本操作执行次数的函数(也可以认为是程序的相对执行时间函数),n 为输入规模,刚才2个场景分别对应了程序的2种执行方式。

function drink1(n) {
   for (let i = 0; i < n; i++) {
       console.log('1小时了')
       console.log('1小时了')
       console.log('喝一升水')     
   }
}

function drink2(n) {
   for (let i = n; i > 1; i/=2) {
       console.log('1小时了')
       console.log('喝一半水')
   }
}

渐进时间复杂度

若存在函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称为O(f(n)),O为算法的渐进时间复杂度,简称为时间复杂度。简言之,就是根据数量级来看,如果同等数量级,则用最高数量级前面的系数比作为渐进时间复杂度。

因为渐进时间复杂度用大写O来表示,所以也被称为大O表示法; 场景1里面: T(n) = n;最高阶项为2n,省去系数1,则转化的时间复杂度为 :T(n)=O(n);

场景2里面:T(n) = logn,最高阶项为logn,省去系数1,则转化的时间复杂度为:T(n) =O(logn)

空间复杂度

在运行一段程序时,我们不仅要执行各种运算指令,同时也会根据需 要,存储一些临时的中间数据 ,以便后续指令可以更方便地继续执 行。给出一组数字,要求去重

 [3,1,2,5,4,9,7,2]

一般通过双重循环也可以做到,但是整个时间复杂度为 n2,即O(n2)。但是这显然不是一种良好的算法,这时就要考虑空间复杂度。

当遍历整个数列时,每遍历一个整数,就把该整数存储起来,就像放到字典中一样。当遍历下一个整数时,不必再慢慢向前回溯比较,而直接 去“字典”中查找,看看有没有对应的整数即可。如果已经遍历了前面7个整数,那么字典里存储的信息如下。 字典左侧key代表值,右边是出现的次数,当遍历到最后一个整数2时,从"字典"可以轻松找到2曾经出现过,那么问题就影人二级,由于读写字典本身的时间复杂度是O(1),所以整个算法的时间复杂度是O(n),和最初相比,运行效率大大提高了。

在递归时,考虑到每次的压栈都要给方法和参数分配空间,则执行n此的话,空间复杂度为n

function fun4(n){
	if(n<=1){
    	return;
    }
    fun4(n-1);
    ...
}

加入传进去的是5,那么方法fun4(参数n=5)的调用信息先入栈。接下来递归调用相同的方法,方法fun4(参数n=4)的调用信息入栈。以此类推,递归越深,入栈元素越多;执行到最后,“方法调用栈”的全部元素会一一出栈。纯粹的递归操作的空间复杂度也是线性的,如果递归的深度是n,那么空间复杂度就是O(n) 。

空间复杂度和时间复杂度要有所取舍,才能更好地服务于性能以及各种体验。