前端算法

JavaScript 数据结构与算法分析

1. 时间复杂度分析

示例代码

O(n) 线性复杂度
javascript 复制代码
function fun1(n) {
  let sum = 0,
    i = 0;
  for (; i <= n; i++) {
    sum += i;
  }
  return sum;
}

2. O(n²) 平方复杂度

js 复制代码
function fun2(n) {
  let sum = 0,
    sum1 = 0,
    i = 0,
    j = 0;
  for (; i <= n; i++) {
    // O(n)
    sum += i;
  }
  for (i = 0; i <= n; i++) {
    // O(n²)
    for (j = 0; j <= n; j++) {
      sum += i * j;
    }
  }
  return sum;
}

3. 嵌套函数 O(n²)

js 复制代码
function fun3(n) {
  let sum = 0,
    i = 0;
  for (; i <= n; i++) {
    // O(n)
    sum += fun(i); // 假设 fun() 是 O(n)
  }
  return sum;
}

复杂度分类 类型 表示法 特征描述 示例 常量阶 O(1) 无循环/递归 简单赋值操作 对数阶 O(logn) 循环变量指数变化 二分查找 线性阶 O(n) 单层循环 数组遍历 线性对数阶 O(nlogn) 分治算法 快速排序 平方阶 O(n²) 双层嵌套循环 冒泡排序 指数阶 O(2ⁿ) 递归翻倍 斐波那契朴素递归 阶乘阶 O(n!) 全排列问题 旅行商问题

4. 空间复杂度分析

常见情况 O(1): 只使用常数个变量

O(n): 使用与输入规模 n 成比例的额外空间

O(n²): 使用二维数组等结构

5. JavaScript 数组实现原理

js 复制代码
// V8 引擎源码摘录
class JSArray : public JSObject {
  DECL_ACCESSORS(length, Object)
  static const int kPreallocatedArrayElements = 4;
};

两种存储模式 模式 数据结构 触发条件 快数组 FixedArray 连续存储,长度在预分配范围内 慢数组 HashTable 稀疏数组/超大索引访问

模式转换条件 快 → 慢:

索引差值 ≥ 1024(index - capacity ≥ 1024)

新容量 > 3 倍旧容量

慢 → 快:

元素可连续存储

节省空间 ≥ 50%

动态扩容机制

js 复制代码
push() 扩容流程:
1. 计算新容量:new_capacity = old_capacity * 1.5 + 16
2. 分配新内存空间
3. 拷贝原有元素
4. 追加新元素
5. length + 1

常用方法复杂度 方法 时间复杂度 说明 push/pop O(1) 可能触发扩容/缩容 shift O(n) 需要移动所有元素 forEach O(n) 线性遍历

6. 栈和队列实现

基于数组的实现

js 复制代码
// 栈实现
let stack = [1, 2, 3];
stack.push(4); // 入栈 O(1)
stack.pop(); // 出栈 O(1)

// 队列实现
let queue = [1, 2, 3];
queue.push(4); // 入队 O(1)
queue.shift(); // 出队 O(n)
  • 优化建议 需要频繁 shift() 时建议使用专门队列库 大数据量操作时注意快/慢数组转换对性能的影响

    7. 数组的方法

    原数组会变

    push() 在尾部追加。入栈。 pop() 在尾部弹出。出栈。

    unshift()在头部压入数据。入队 shift()在头部弹出数据。出队

    reverse()翻转原数组,并返回已完成翻转的数组。 .splice(start, deleteCount, item1, item2......) start 参数 开始的位置 deleteCount 要截取的个数 后面的 items 为要添加的元素 如果 deleteCount 为 0,则表示不删除元素,从 start 位置开始添加后面的几个元素到原始的数组里面。 返回值为由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。

    js 复制代码
    const arr3 = [1, 2, 3, 4, 5, 6, 7, "f1", "f2"];
    const arr4 = arr3.splice(2, 3); // 删除第三个元素以后的三个数组元素(包含第三个元素)
    console.log(arr4); // [3, 4, 5];
    console.log(arr3); // [1, 2, 6, 7, "f1", "f2"]; 原始数组被改变
    
    const arr5 = arr3.splice(2, 0, "wu", "leon");
    // 从第2位开始删除0个元素,插入"wu","leon"
    console.log(arr5); // [] 返回空数组
    console.log(arr3); // [1, 2, "wu", "leon", 6, 7, "f1", "f2"]; 原始数组被改变
    
    const arr6 = arr3.splice(2, 3, "xiao", "long");
    // 从第 2 位开始删除 3 个元素,插入"xiao", "long"
    console.log(arr6); // ["wu", "leon", 6]
    console.log(arr3); //[ 1, 2, "xiao", "long", 7, "f1", "f2"]
    
    const arr7 = arr3.splice(2); // 从第三个元素开始删除所有的元素
    console.log(arr7); // ["xiao", "long", 7, "f1", "f2"]
    console.log(arr3); // [1, 2]

    sort()对数组的元素进行排序,并返回数组。

js 复制代码
const arr = [1, 2, 3];
arr.sort((a, b) => b - a);
console.log(arr); // [3, 2, 1]

原数组不变

concat()会在当前数组尾部拼接传入的数组,然后返回一个新数组 indexOf()在数组中寻找该值,找到则返回其下标,找不到则返回-1。

js 复制代码
const arr = [1, 2, 3];
console.log(arr.indexOf(2)); // 1
console.log(arr.indexOf(0)); // -1

includes()在数组中寻找该值,找到则返回 true,找不到则返回 false。

js 复制代码
const arr = [1, 2, 3];
console.log(arr.includes(2)); // true
console.log(arr.includes(4)); // false

join()将数组转化成字符串,并返回该字符串,不传值则默认逗号隔开。

js 复制代码
const arr = [1, 2, 3];
console.log(arr.join()); // '1, 2, 3'
console.log(arr); // [1, 2, 3]

.slice(start,end)从 start 开始截取到 end,但是不包括 end

js 复制代码
const arr = [1, 2, 3, 4, 5];
console.log(arr.slice(1, 4)); // [2, 3, 4]
console.log(arr); // [1, 2, 3, 4, 5]

toString()将数组转化成字符串,并返回该字符串,逗号隔开。

js 复制代码
const arr = [1, 2, 3, 4, 5];
console.log(arr.toString()); // '1, 2, 3, 4, 5'
console.log(arr); // [1, 2, 3, 4, 5]

7. 字符串常用方法

.charAt()返回指定索引位置处的字符。类似于数组用中括号获取相应下标位置的数据。

js 复制代码
var str = "abcdefg";
console.log(str.charAt(2)); // 输出 'c'
console.log(str[2]); // 输出 'c'

.concat()类似数组的 concat(),用来返回一个合并拼接两个或两个以上字符串

js 复制代码
const str1 = "abcdefg";
const str2 = "1234567";
const str3 = str1.concat(str2);
console.log(str3); // 输出 'abcdefg1234567'

.indexOf()、lastIndexOf()indexOf,返回一个字符在字符串中首次出现的位置,lastIndexOf 返回一个字符在字符串中最后一次出现的位置。

js 复制代码
const str = "abcdcefcg";
console.log(str.indexOf("c")); // 输出 '2'
console.log(str.lastIndexOf("c")); // 输出 '7'

.slice()提取字符串的片断,并把提取的字符串作为新的字符串返回出来。原字符串不变。

js 复制代码
const str = "abcdefg";
console.log(str.slice()); // 输出 'abcdefg', 不传递参数默认复制整个字符串
console.log(str.slice(1)); // 输出 'bcdefg',传递一个,则为提取的起点,然后到字符串结尾
console.log(str.slice(2, str.length - 1)); // 输出'cdef',传递两个,为提取的起始点和结束点

.split()使用指定的分隔符将一个字符串拆分为多个子字符串数组并返回,原字符串不变。

js 复制代码
const str = "A*B*C*D*E*F*G";
console.log(str.split("*")); // 输出 ["A", "B", "C", "D", "E", "F", "G"]

.substr(), substring() 这两个方法的功能都是截取一个字符串的片段,并返回截取的字符串。 substr 和 substring 这两个方法不同的地方就在于参数二,substr 的参数二是截取返回出来的这个字符串指定的长度,substring 的参数二是截取返回这个字符串的结束点,并且不包含这个结束点。而它们的参数一,都是一样的功能,截取的起始位置。 注意事项:substr 的参数二如果为 0 或者负数,则返回一个空字符串,如果未填入,则会截取到字符串的结尾去。substring 的参数一和参数二为 NAN 或者负数,那么它将被替换为 0。

js 复制代码
const str = "ABCDEFGHIJKLMN";
console.log(str.substr(2)); // 输出 'CDEFGHIJKLMN'
console.log(str.substring(2)); // 输出 'CDEFGHIJKLMN'

console.log(str.substr(2, 9)); // 输出 'CDEFGHIJK'
console.log(str.substring(2, 9)); // 输出 'CDEFGHI'

.match()方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配,并返回一个包含该搜索结果的数组。

js 复制代码
const str = "2018年结束了,2019年开始了,2020年就也不远了";
const reg = /\d+/g; // 这里是定义匹配规则,匹配字符串里的1到多个数字
console.log(str.match(reg)); // 输出符合匹配规则的内容,以数组形式返回 ['2018', '2019', '2020']
console.log(str.match("20")); // 不使用正则 ["20", index: 0, input: "2018年结束了,2019年开始了"]

注意事项:如果 match 方法没有找到匹配,将返回 null。如果找到匹配,则 match 方法会把匹配到以数组形式返回,如果正则规则未设置全局修饰符 g,则 match 方法返回的数组有两个特性:input 和 index。input 属性包含整个被搜索的字符串。index 属性包含了在整个被搜索字符串中匹配的子字符串的位置。

.replace()接收两个参数,参数一是需要替换掉的字符或者一个正则的匹配规则,参数二,需要替换进去的字符,仔实际的原理当中,参数二,你可以换成一个回调函数。

js 复制代码
const str = "2018年结束了,2019年开始了,2020年就也不远了";
const rex = /\d+/g; // 这里是定义匹配规则,匹配字符串里的1到多个数字
const str1 = str.replace(rex, "****");
console.log(str1); // 输出:"****年结束了,****年开始了,****年也不远了"
const str2 = str.replace(rex, function (item) {
  console.log(arguments); // 看下面的图片
  const arr = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
  let newStr = "";
  item.split("").map(function (i) {
    newStr += arr[i];
  });
  return newStr;
});
console.log(str2); // 输出:贰零壹捌年结束了,贰零壹玖年开始了,贰零贰零年也不远了

.search()在目标字符串中搜索与正则规则相匹配的字符,搜索到,则返回第一个匹配项在目标字符串当中的位置,没有搜索到则返回一个-1。

js 复制代码
const str = "2018年结束了,2019年开始了,2020年就也不远了";
const reg = /\d+/i; // 这里是定义匹配规则,匹配字符串里的1到多个数字
console.log(str.search(reg)); // 输出 0  这里搜索到的第一项是从位置0开始的

.toLowerCase(),toUpperCase()toLowerCase 把字母转换成小写,toUpperCase()则是把字母转换成大写。

js 复制代码
const str1 = "abcdefg";
const str2 = "ABCDEFG";
console.log(str2.toLowerCase()); // 输出:'abcdefg'
console.log(str1.toUpperCase()); // 输出:'ABCDEFG'

.includes(), startsWith(), endsWith()includes、startsWith、endsWith,es6 的新增方法,includes 用来检测目标字符串对象是否包含某个字符,返回一个布尔值,startsWith 用来检测当前字符是否是目标字符串的起始部分,相对的 endwith 是用来检测是否是目标字符串的结尾部分。

js 复制代码
const str = "Excuse me, how do I get to park road?";
console.log(str.includes("how")); // 输出:true
console.log(str.startsWith("Excuse")); // 输出: true
console.log(str.endsWith("?")); // 输出: true

.repeat()返回一个新的字符串对象,新字符串等于重复了指定次数的原始字符串。接收一个参数,就是指定重复的次数。原字符串不变。

js 复制代码
const str = "http";
const str2 = str.repeat(3);
console.log(str); // 输出:'http'
console.log(str2); // 输出:'httphttphttp'
相关推荐
zengyuhan50327 分钟前
Windows BLE 开发指南(Rust windows-rs)
前端·rust
醉方休30 分钟前
Webpack loader 的执行机制
前端·webpack·rust
前端老宋Running39 分钟前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔39 分钟前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户44455436542641 分钟前
Android的自定义View
前端
WILLF42 分钟前
HTML iframe 标签
前端·javascript
枫,为落叶1 小时前
Axios使用教程(一)
前端
小章鱼学前端1 小时前
2025 年最新 Fabric.js 实战:一个完整可上线的图片选区标注组件(含全部源码).
前端·vue.js
ohyeah1 小时前
JavaScript 词法作用域、作用域链与闭包:从代码看机制
前端·javascript
流星稍逝1 小时前
手搓一个简简单单进度条
前端