如何用JavaScript学习算法复杂度


Posted in Javascript onApril 30, 2021

概述

在本文中,我们将探讨 “二次方” 和 “n log(n)” 等术语在算法中的含义。

在后面的例子中,我将引用这两个数组,一个包含 5 个元素,另一个包含 50 个元素。我还会用到JavaScript中方便的performance API来衡量执行时间的差异。

const smArr = [5, 3, 2, 35, 2];

const bigArr = [5, 3, 2, 35, 2, 5, 3, 2, 35, 2, 5, 3, 2, 35, 2, 5, 3, 2, 35, 2, 5, 3, 2, 35, 2, 5, 3, 2, 35, 2, 5, 3, 2, 35, 2, 5, 3, 2, 35, 2, 5, 3, 2, 35, 2, 5, 3, 2, 35, 2];

什么是 Big O 符号?

Big O 表示法是用来表示随着数据集的增加,计算任务难度总体增长的一种方式。尽管还有其他表示法,但通常 big O 表示法是最常用的,因为它着眼于最坏的情况,更容易量化和考虑。最坏的情况意味着完成任务需要最多的操作次数;如果你在一秒钟内就能恢复打乱魔方,那么你只拧了一圈的话,不能说自己是做得最好的。

当你进一步了解算法时,就会发现这非常有用,因为在理解这种关系的同时去编写代码,就能知道时间都花在了什么地方。

当你了解更多有关 Big O 表示法的信息时,可能会看到下图中不同的变化。我们希望将复杂度保持在尽可能低的水平,最好避免超过 O(n)。

如何用JavaScript学习算法复杂度

O(1)

这是理想的情况,无论有多少个项目,不管是一个还是一百万个,完成的时间量都将保持不变。执行单个操作的大多数操作都是 O(1)。把数据写到数组、在特定索引处获取项目、添加子元素等都将会花费相同的时间量,这与数组的长度无关。

const a1 = performance.now();
smArr.push(27);
const a2 = performance.now();
console.log(`Time: ${a2 - a1}`); // Less than 1 Millisecond


const b1 = performance.now();
bigArr.push(27);
const b2 = performance.now();
console.log(`Time: ${b2 - b1}`); // Less than 1 Millisecond

O(n)

在默认情况下,所有的循环都是线性增长的,因为数据的大小和完成的时间之间存在一对一的关系。所以如果你有 1,000 个数组项,将会花费的 1,000 倍时间。

const a1 = performance.now();
smArr.forEach(item => console.log(item));
const a2 = performance.now();
console.log(`Time: ${a2 - a1}`); // 3 Milliseconds

const b1 = performance.now();
bigArr.forEach(item => console.log(item));
const b2 = performance.now();
console.log(`Time: ${b2 - b1}`); // 13 Milliseconds

O(n^2)

指数增长是一个陷阱,我们都掉进去过。你是否需要为数组中的每个项目找到匹配对?将循环放入循环中是一种很好的方式,可以把 1000 个项目的数组变成一百万个操作搜索,这将会使你的浏览器失去响应。与使用双重嵌套循环进行一百万次操作相比,最好在两个单独的循环中进行 2,000 次操作。

const a1 = performance.now();
smArr.forEach(() => {
    arr2.forEach(item => console.log(item));
});
const a2 = performance.now();
console.log(`Time: ${a2 - a1}`); // 8 Milliseconds


const b1 = performance.now();
bigArr.forEach(() => {
    arr2.forEach(item => console.log(item));
});
const b2 = performance.now();
console.log(`Time: ${b2 - b1}`); // 307 Milliseconds

O(log n)

我认为关于对数增长最好的比喻,是想象在字典中查找像 “notation” 之类的单词。你不会在一个词条一个词条的去进行搜索,而是先找到 “N” 这一部分,然后是 “OPQ” 这一页,然后按字母顺序搜索列表直到找到匹配项。

通过这种“分而治之”的方法,找到某些内容的时间仍然会因字典的大小而改变,但远不及 O(n) 。因为它会在不查看大部分数据的情况下逐步搜索更具体的部分,所以搜索一千个项目可能需要少于 10 个操作,而一百万个项目可能需要少于 20 个操作,这使你的效率最大化。

在这个例子中,我们可以做一个简单的快速排序。

const sort = arr => {
  if (arr.length < 2) return arr;

  let pivot = arr[0];
  let left = [];
  let right = [];

  for (let i = 1, total = arr.length; i < total; i++) {
    if (arr[i] < pivot) left.push(arr[i]);
    else right.push(arr[i]);
  };
  return [
    ...sort(left),
    pivot,
    ...sort(right)
  ];
};
sort(smArr); // 0 Milliseconds
sort(bigArr); // 1 Millisecond

O(n!)

最糟糕的一种可能性是析因增长。最经典的例子就是旅行的推销员问题。如果你要在很多距离不同的城市之间旅行,如何找到在所有城市之间返回起点的最短路线?暴力方法将是检查每个城市之间所有可能的路线距离,这是一个阶乘并且很快就会失控。

由于这个问题很快会变得非常复杂,因此我们将通过简短的递归函数演示这种复杂性。这个函数会将一个数字去乘以函数自己,然后将数字减去1。阶乘中的每个数字都会这样计算,直到为 0,并且每个递归层都会把其乘积添加到原始数字中。

阶乘只是从 1 开始直至该数字的乘积。那么6!是1x2x3x4x5x6 = 720。

const factorial = n => {
  let num = n;

  if (n === 0) return 1
  for (let i = 0; i < n; i++) {
    num = n * factorial(n - 1);
  };

  return num;
};
factorial(1); // 2 Milliseconds
factorial(5); // 3 Milliseconds
factorial(10); // 85 Milliseconds
factorial(12); //  11,942 Milliseconds

我原本打算显示factorial(15),但是 12 以上的值都太多,并且使页面崩溃了,这也证明了为什么需要避免这种情况。

结束语

我们需要编写高性能的代码似乎是一个不争得事实,但是我敢肯定,几乎每个开发人员都创建过至少两重甚至三重嵌套循环,因为“它确实有效”。Big O 表示法在表达和考虑复杂性方面是非常必要的,这是我们从未有过的方式。

以上就是如何用JavaScript学习算法复杂度的详细内容,更多关于JS算法复杂度的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
extJs 下拉框联动实现代码
Apr 09 Javascript
js将当前时间格式转换成时间搓(自写)
Sep 26 Javascript
javascript实现带节日和农历的日历特效
Feb 01 Javascript
纯javascript实现的小游戏《Flappy Pig》实例
Jul 27 Javascript
javascript html实现网页版日历代码
Mar 08 Javascript
JS判断输入字符串长度实例代码(汉字算两个字符,字母数字算一个)
Aug 02 Javascript
JS实现六边形3D拖拽翻转效果的方法
Sep 11 Javascript
Vue.js Ajax动态参数与列表显示实现方法
Oct 20 Javascript
vue中路由验证和相应拦截的使用详解
Dec 13 Javascript
react 父子组件之间通讯props
Sep 08 Javascript
JQuery常见节点操作实例分析
May 15 jQuery
jQuery实现电梯导航模块
Dec 22 jQuery
JS不要再到处使用绝对等于运算符了
Apr 30 #Javascript
如何用Node.js编写内存效率高的应用程序
用几道面试题来看JavaScript执行机制
Apr 30 #Javascript
详解前端任务构建利器Gulp.js使用指南
Apr 30 #Javascript
浅谈node.js中间件有哪些类型
Apr 29 #Javascript
JavaScript实现简单图片切换
何时使用Map来代替普通的JS对象
You might like
百事可乐也出咖啡了 双倍咖啡因双倍快乐
2021/03/03 咖啡文化
2019年中国咖啡业现状与发展趋势
2021/03/04 咖啡文化
php数据库连接时容易出错的特殊符号问题
2010/09/01 PHP
PHP版网站缓存加快打开速度的方法分享
2012/06/03 PHP
删除html标签得到纯文本可处理嵌套的标签
2014/04/28 PHP
PHP的serialize序列化数据以及JSON格式化数据分析
2015/10/10 PHP
新闻内页-JS分页
2006/06/07 Javascript
javascript:void(0)的真正含义实例分析
2008/08/20 Javascript
JS 实现完美include载入实现代码
2010/08/05 Javascript
jQuery - css() 方法示例详解
2014/01/16 Javascript
JavaScript实现的图像模糊算法代码分享
2014/04/22 Javascript
jquery实现搜索框常见效果的方法
2015/01/22 Javascript
Javascript使用uploadify来实现多文件上传
2016/11/16 Javascript
switchery按钮的使用方法
2017/12/18 Javascript
Vue使用枚举类型实现HTML下拉框步骤详解
2018/02/05 Javascript
解决vue中无法动态修改jqgrid组件 url地址的问题
2018/03/01 Javascript
微信小程序如何获取手机验证码
2018/11/04 Javascript
微信小程序如何访问公众号文章
2019/07/08 Javascript
[01:48:04]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Elephant BO3 第一场 2月7日
2021/03/11 DOTA
Djang中静态文件配置方法
2015/07/30 Python
在windows系统中实现python3安装lxml
2016/03/23 Python
python分布式环境下的限流器的示例
2017/10/26 Python
Python实现PS图像调整之对比度调整功能示例
2018/01/26 Python
python求最大连续子数组的和
2018/07/07 Python
Pandas实现DataFrame按行求百分数(比例数)
2019/12/27 Python
python sorted函数原理解析及练习
2020/02/10 Python
Python模块相关知识点小结
2020/03/09 Python
python 统计list中各个元素出现的次数的几种方法
2021/02/20 Python
GoDaddy英国:全球排名第一的域名注册商
2018/06/08 全球购物
初中同学会活动方案
2014/08/22 职场文书
法学专业大学生实习自我鉴定
2014/10/05 职场文书
电子商务专业求职信范文
2015/03/19 职场文书
婚育证明样本
2015/06/16 职场文书
离婚起诉书范文2016
2015/11/26 职场文书
2016五一手机促销广告语
2016/01/28 职场文书
Win2008系统搭建DHCP服务器
2022/06/25 Servers