算常数总和-性能好-时间复杂度 O(1):
飞蛾公式: (n / 2) *(1 + n)
题库:leetcode
- 性能上:for > forEach > map
- for in 是键值的遍历,for of 是 es6 的产物,是迭代器的遍历方式,需要判断遍历对象是否存在
- 本质上 forEach 和 map 是用来遍历 Array
- 反转字符串:str.split(‘’).reduceRight((prev,curr)=>prev+curr)
时/空间复杂度
计算时间复杂度?
1 | const numbers = [1, 3, 10, 50] |
1 | // n = 5 |
总结: 使用渐进分析法给每种算法分析以下 3 种情况,最后得到时间复杂度
- Best Case: number = 1 OR number = 2 => O(1) // 数值少
- Average Case: O(n)
- Worst Case: number = 27,277 => O(n) (Improved: O(sqrt(n))) // 数值大
判断是否为 2 的幂
- 使用位运算符:二进制 与 &
- &: 按位相乘,有 0 为 0,示例:
4 & 7 ==> 100 & 111 ==> 100 ==> 4(最终结果)
- number & (number - 1) === 0: 始终返回 true
1 | function isPowerOfTwo(number) { |
递归算法
1 | function fact(number) { |
1 | function fib(n) { |
动态编程(Dynamic Programming)
在第一个函数也就是该方法的全局中添加存储空间的变量
在添加一个记忆变量的形式变量
在每次该回调函数结束前,把存储变量赋值给记忆变量
判断记忆变量有这个值就返回记忆变量的值不执行后面的内容避免冗余
以递归实现的斐波那契为例进行改造
1 | function fib(n, memo) { |
建立一个存储所有这些值的存储,将所有结果存储在此存储中的 bonachi 序列。执行方法的过程中,不断生成数据,知道目标值生成完成函数执行。当数据够多,一旦检索到 nf 元素然后我们取消完成函数执行。
- 使用场景:
- 在循环中使用底部应用程序方法
- 带有递归的动态规划方法
搜索算法
线性搜索(可以是任意类型的数据)
- 从头到尾遍历查找,找到对应的值就返回并停止,只返回第一个目标结果就停止
- 适用于有序和无序列表,所以列表不必排序
- 当然可以用系统内置的方法 find(), findIndex()
1 | // 搜索对象 |
需要先排序,然后根据中有两边的值,从中间划分查找
- 基于循环
1 | const arr = [1, 5, 9, 13, 99, 100] |
- 递归的二分查找
1 | const arr = [1, 5, 9, 13, 99, 100] |
与基于循环的解决方案的主要区别: 是在循环中只是改变了索引然后计算不同的中间指标
在递归方案中总是计算相同的中间指标,改变 index 来调用新函数,再次使用原始数组的子集。即真的切数组为不同函数调用分成切片,在不同的数组切片上操作
主定理(Master Theorem)
递归的运行时间: O(nlogba)
公式里的变量知识面意思?
- a:等于子问题数(递归拆分数)多久拆分一次数组
- b:等于相对子问题大小(每次拆分的输入减少)
- f(n):等于递归外部运行时,指整个代码
使用主定理得到算法的整体时间复杂度的三种情况:
- case 1:递归步骤,完成最多工作,具有最大的运行时间,算法的运行时间为 O(nlogba)
- case 2:非递归部分做更多工作:O(fn(n))
- case 3:递归开始时的相同工作量推到整个算法的运行时间 O(nlogbalog n)
排序算法
1 | function sort(arr) { |
1 | function sort(arr) { |
1 | function sort(arr) { |
空间复杂度
集合 sets(数组)算法
1 | // 衣服制造问题--衣服尺码颜色组合问题 |
1 | function getPermutations(options) { |
1 | function getPermutations(options, length) { |
复杂的算法和解决的方法
排列出从所给的 items 中拿出不超过背包重量的 item
1 | function knapsack(items, cap, itemIndex) { |
- 改进版
1 | function knapsackFn(items, cap, itemIndex, memo) { |
1 | // 一次只能返回一个满足要求的选项 |
1 | // 存储对象的值 |
1 | // 存储对象的值 |
Set 和 new Map
- 并集:利用 set 特性给两个数组去重
1 | const arr1 = [1, 22, 38, 31, 18] |
交集:
-
const intersection = new Set([...setArr1].filter((item) => setArr2.has(item)))
-
差集:
const intersection = new Set([...setArr1].filter((item) => !setArr2.has(item)))
判断元素是否存在,时间复杂度为 O(1),速度快
1 | const nums = [11, 22, 38, 1, 8] |
- 实现队列,队尾添加,队头删除/栈的数据结构,在表尾即可完成添加和删除
1 | const alignment = new Set() |
1 | // 这种数据结构适用 |
Object 与 new Map
- 创建一个 new Map(),可以适用.set(key,value)设置该 Map 的键值,使用.get(key)查找 Map 中该键的值,.has 是否存在该键
- Map 的键值支持正则,Map 是可迭代的,例如:forEach 循环 for…of…循环 ,Object 是不能直接迭代的
- 适用场景:
- 当插入的顺序是需要考虑的,并且使用除 String 和 Symbol 以外的键名是,使用 Map
- 需要遍历键值对并且需要考虑顺序,优先 Map
- 频繁增删改查的场景使用 Map
- 表单自定义字段使用 Map
1 | const map4 = new Map() |
元素的顺序和长度
- Map 保持对长度的跟踪,使其能够在 O(1)复杂度中进行访问
- Object 想要获得对象的属性长度,需要手动进行迭代,使其文 O(n)复杂度,属性长度为 n
- Map 始终保持按插入顺序返回键名
- Object 从 es6 开始,String 和 Symbol 键是按顺序保存起来的,但是通过隐式转换保存成 String 的键是乱序的
Set 的时间复杂度
Set 操作 时间复杂度 Set.prototype.get() O(1) ~ O(logn) Set.prototype.add() O(1) ~ O(logn) Set.prototype.has() O(1) ~ O(logn) Set.prototype.forEach() O(n) Set.prototype.entries() O(n) Set.prototype.values() O(n) Set.prototype.keys() O(n)
哈希表
- 使用一层循环来获取需要双层循环的结果
- 列举:求两数之和:
- 使用 new Map() 创建一个空的 map 数组
- 在遍历的过程中通过 map 数组使用 has 方法判断 target-数组中的某一项得到的值是否存在
- 存在就使用 get 方法获取对应值的小标并返回
- 不存在就使用 set 方法设置下标给 map 数组
- 如果把 for 循环换成 forEach 有问题
1 | var twoSum = function (nums, target) { |
回溯算法
- 本质:暴力穷举算法
- 列如:给定两个整数 n 和 k,返回范围[1,n]中所有可能的 k 的组合,不要求顺序
- 其中:k 指的是组合的长度,如[2,4],并且按照数学规范[最小值,最大值], n 指最大区间
1 | const combine = (n, k) => { |
动态规划 DP 算法
- 设定一个数组,存放到达第 n 层的跳法,假定只能从当前元素的左和下移动
- 找一个位置,分析到达这个位置的前置条件,并将这些条件以二维数组的方式相加
- 例如: a[i][j] = a[i-1][j] + a[i][j-1]
- 倒着分析,找关系
- 处理极值
- arr[i][0] = 1
- arr[0][j] = 1
1 | // 生成一个二维数据的空数组 |