前端算法

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'
相关推荐
a别念m31 分钟前
webpack基础与进阶
前端·webpack·node.js
芭拉拉小魔仙44 分钟前
【Vue3/Typescript】从零开始搭建H5移动端项目
前端·vue.js·typescript·vant
axinawang1 小时前
通过RedisCacheManager自定义缓存序列化(适用通过注解缓存数据)
前端·spring·bootstrap
前端南玖1 小时前
Vue3响应式核心:ref vs reactive深度对比
前端·javascript·vue.js
哔哩哔哩技术1 小时前
B站在KMP跨平台的业务实践之路
前端
微笑边缘的金元宝1 小时前
svg实现3环进度图,可动态调节进度数值,(vue)
前端·javascript·vue.js·svg
程序猿小D1 小时前
第28节 Node.js 文件系统
服务器·前端·javascript·vscode·node.js·编辑器·vim
Trae首席推荐官1 小时前
字节跳动技术副总裁洪定坤:TRAE 想做 AI Development
前端·人工智能·trae
小妖6661 小时前
uni-app bitmap.load() 返回 code=-100
前端·javascript·uni-app
前端与小赵1 小时前
uni-app隐藏返回按钮
前端·uni-app