JS数据结构与算法丨时间复杂度与空间复杂度

fc818bf1528dd7957ebb22669b3276e4.png

JS 数据结构与算法教程将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!


数据结构是一种具备一定的逻辑结构的数据元素集合,这个结构指的是数据元素之间的关系,分为逻辑结构和存储结构。

算法是用来解决问题的,有简单的,比如找出数组中的最大值;也有复杂的,比如找出起始点到目标点所有路径中的最短路径。

算法的设计取决于数据的逻辑结构,而算法的实现取决于数据的存储结构。逻辑结构是指数据之间的逻辑关系,就是我们上面所说的一对一、一对多等。存储结构是指我们这个数据在计算机中的存储形式,比如顺序存储、链式存储、索引存储、哈希存储等。

数据结构与算法是每一名计算机工程师必备的技能,本系列采用JavaScript编程语言,现在就让我们开始学习数据结构与算法‍吧!


怎么衡量算法的好坏

算法是变化莫测的,每一个问题都有很多种解决方案。


比如计算 1-100 的和。第一种,我们一个一个累加计算1+2+3+...;第二种,我们采用等差数列求和方式,(1+100)+(2+99)+(3+98)+...。那么上面两种,哪一个是更加高效的呢?显而易见,第二种更加高效,虽然第一种最终也可以得到正确答案,但是其计算过程要低效的多。

在计算机中,我们衡量一个算法的好坏,有两个重要标准:

  • 时间复杂度

  • 空间复杂度

顾名思义,时间复杂度指的是我们的算法的执行时间;空间复杂度指的是我们的算法所占用的内存空间大小。

但是,当代码还没有运行的时候,我们不可能知道我们的代码所执行的时间和空间的。这个时候,我们就需要根据输入规模的大小,来预估算法的基本操作执行次数。


渐进时间复杂度

渐进时间复杂度就是我们处理一个问题时,所用时间的一个方程组,记作T(n)。

比如,你在吃瓜子,每秒钟你可以吃一粒瓜子,那么10个瓜子你需要多长时间?10秒钟。 那么如果n个瓜子呢?就是n秒钟。记作,T(n) = n。

再比如,你还在吃瓜子,你每分钟吃一半,那么把瓜子吃的只剩下一粒,你需要多长时间?这里我们分解一下,第一次:吃8个,第二次:吃4个;第三次:吃两个;第四次;吃一个;此时,剩下的瓜子就只有一个了。我们就是16不断的除以2,记作log16。那么如果n个瓜子呢?就是logn。记作,T(n) =logn。

第三个例子,你还在吃瓜子,你吃第一个瓜子需要1分钟,吃第二个瓜子要两分钟,吃第三个瓜子要三分钟...,那么10个瓜子你需要多长时间呢?答案是1+2+3+4+...+10 = 55分钟。那么如果n个瓜子呢?就是1+2+3+...+n = n*(1+n)/2 = 0.5n^2 + 0.5n。记作,T(n) = 0.5n^2 + 0.5n。

若存在函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称为O(f(n)),它随着问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称做算法的渐进时间复杂度,渐进就是算法规模一点点变大的过程,简称为时间复杂度。

上面三个例子的时间复杂度,分别是 O(n)、O(logn)、O(n^2)。第三个例子中,0.5n^2 + 0.5n,0.5n^2相比0.5n来说,所占的权重更大,所以可以省略0.5n,而 0.5n^2,我们也可以省略系数 0.5,所以最终结果为 O(n^2)。只保留T(n)中的最高阶项,如果最高阶项存在,则省去最高阶项前面的系数。这里涉及到高等数学的求极限概念。


计算时间复杂度

我们先举个小例子:

// 例 1
{ ++a }
// 例 2
for(let i = 1;i<n;i++){ 
  ++a 
}
// 例 3
for(let i = 1;i<n){
  for(let j = 1;j<n){
    ++a
  }
}

我们来看上面代码中,"++a" 执行的次数。例1中,执行了 1 次;例 2 中,执行了 n 次;例 3 中,执行了 n^2 次。相对应的,例1中的算法的时间复杂度就是O(1),例 2 是 O(n),例3是 O(n^2)。 

计算算法的时间复杂度的时候,一般情况下,我们只需要找到一种基本操作来讨论算法的时间复杂度即可,这里我们找到的是“a增加1”的次数。对于复杂的算法而言,也可以找几种基本操作,来反映执行不同操作所需的时间,让我们的算法衡量更准确全面。


什么是空间复杂度

顾名思义,空间复杂度就是算法中保存的临时变量所占据的内存容量的大小。记作 S(n) = O(f(n)) 。


计算空间复杂度

空间复杂度也是随着问题的规模呈一定的变化趋势的,比如下面的几个例子。

// 例 1 
function fn(n){
  let arr = 1
}


// 例 2
function fn(n){
  let arr = new Array(n)
} 
// 例 3
function fn(n){
  let arr = new Array(n)
  arr.forEach(item=>{
    item = new Array(n)
  })
}
// 例 4
function fn(n){
  if(n<=1) return
  fn(n-1)
}

例 1 中,变量所占用的空间大小为规定值1,所以它的空间复杂度记作O(1);

例 2 中,变量所占用的空间大小与输入的规模成正比,记作O(n);

例 3 中,变量所占用的空间大小是一个二维空间,记作O(n^2);

例 4 中,算法是一个递归算法,当我们 fn() 函数递归调用的时候,会专门分配一块内存,来存储方法调用栈,这时候所占用的空间大小和我们的递归深度成正比,如果递归深度为n,空间复杂度则记作 O(n)。


JS 数据结构与算法教程‍将在本号持续发布,一起查漏补缺学个痛快!若您有遇到其它相关问题,非常欢迎在评论中留言讨论,达到帮助更多人的目的。若感本文对您有所帮助请点个赞吧!

d2b828d925707ab7e8ac8742885ff2d7.png

叶阳辉

HFun 前端攻城狮

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值