前端面试算法手撕全解(高频真题+标准答案)

摘要:本文汇总前端面试中 95% 的高频手撕算法真题,覆盖数组、字符串、递归、动态规划、工程工具及模拟手写六大方向。每道题均配有业务场景、解题思路与可直接运行的完整代码,并附面试话术,帮助读者快速背诵默写、从容应对笔试与手撕环节。

前端面试算法手撕全解(高频真题+标准答案)

前言

前端面试算法不考困难算法、不考复杂数据结构 ,全部集中在:数组、字符串、递归、遍历、模拟、简单DP、工程工具手写 。本文档汇总95% 前端手撕真题,每道题包含:场景、思路、完整可运行代码、复杂度分析、面试话术,可直接背诵默写。

阅读建议:先通读「一、面试特点」建立全局认知,再按「九、刷题优先级」的顺序逐题攻克。每道题建议先自己手写一遍,再对照标准答案查漏补缺,最后用「八、四段式答题法」口头复述一遍,做到"写得对、讲得清"。

一、前端算法面试特点(必看)

  • 重点:实用性算法,全部来自业务开发 + 笔试真题

  • 核心能力:遍历、递归、条件判断、数据格式化、边界处理

  • 手撕评分标准:代码完整、无BUG、能处理边界、能讲清思路、知道业务场景

二、数组高频算法(最常考)

解题思路 :利用 Set 天然去重特性,一行代码搞定;filter + indexOf 通过判断元素首次出现位置是否等于当前索引来去重,原理清晰;Map 写法利用 map.has() 判断是否已存在,效率更高。

复杂度分析:三种写法时间复杂度均为 O(n),空间复杂度均为 O(n)。

面试话术:"去重我一般先答 Set 最简写法,再补充 filter + indexOf 讲原理,最后用 Map 体现对引用类型和效率的考虑,形成梯度回答。"

解题思路:遍历数组,遇到子数组就递归展开并拼接结果,遇到普通元素直接 push,直到所有层级都被展开。

复杂度分析:时间复杂度 O(n),其中 n 为展开后所有元素的个数;空间复杂度 O(n)(递归调用栈深度取决于嵌套层级)。

面试话术 :"扁平化考察递归思想,我会先写递归版本,再提一句可以用 arr.flat(Infinity) 一行实现,体现对原生 API 的熟悉。"

解题思路 :求和用 reduce 累加,简洁且性能好;最大值用 Math.max 配合展开运算符,注意空数组会返回 -Infinity,可加默认值兜底。

复杂度分析:两者时间复杂度均为 O(n),空间复杂度 O(1)。

面试话术 :"求和与最大值是 reduce 和展开运算符的经典应用,我会顺带提一句边界处理------空数组时 Math.max(...[]) 返回 -Infinity,实际开发要加默认值。"

解题思路 :用 Map 记录已出现过的 key 值,filter 遍历时只保留第一次出现的元素,实现按指定字段去重。

复杂度分析:时间复杂度 O(n),空间复杂度 O(n)。

面试话术 :"对象去重是业务里最高频的场景,我会强调用 Map 而不是 Set,因为 Map 可以按任意字段(如 id)去重,更灵活。"

三、字符串高频算法

1. 字符串反转

场景:笔试基础题、密码反转、文本处理

javascript 复制代码
function reverseStr(str) {
  return str.split('').reverse().join('');
}

2. 回文字符串判断

定义:正读、反读完全一致

javascript 复制代码
function isPalindrome(str) {
  return str === reverseStr(str);
}

3. 统计字符串出现最多字符

场景:文本统计、关键词分析

javascript 复制代码
function getMaxChar(str) {
  const obj = {};
  let maxNum = 0;
  let maxChar = '';
  for (let char of str) {
    obj[char] = obj[char] ? obj[char] + 1 : 1;
    if (obj[char] > maxNum) {
      maxNum = obj[char];
      maxChar = char;
    }
  }
  return { maxChar, maxNum };
}

四、递归经典面试题(前端必考)

1. 递归深拷贝(终极手写)

业务场景:复杂表单数据备份、树形结构复制、状态缓存、防止原数据污染

面试亮点:处理基础类型、数组、对象、日期、正则,边界齐全

javascript 复制代码
function deepClone(obj) {
  // 1. 基础类型 / null 直接返回
  if (obj === null || typeof obj !== 'object') return obj;

  // 2. 处理特殊引用类型
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);

  // 3. 创建对应容器
  const newObj = Array.isArray(obj) ? [] : {};

  // 4. 递归遍历
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      newObj[key] = deepClone(obj[key]);
    }
  }
  return newObj;
}

2. 递归遍历树形结构

场景:后台菜单、权限树、分类树、树形筛选

javascript 复制代码
// 遍历树形数据,执行回调
function traverseTree(list, callback) {
  list.forEach(item => {
    callback(item);
    if (item.children && item.children.length) {
      traverseTree(item.children, callback);
    }
  });
}

五、动态规划 & 笔试经典题

1. 最大连续子数组和(LeetCode简单经典)

场景:前端笔试高频压轴简单题

javascript 复制代码
function maxSubArray(nums) {
  let max = nums[0];
  let sum = 0;
  for (let i = 0; i < nums.length; i++) {
    // 要么单独当前数,要么累加之前
    sum = Math.max(sum + nums[i], nums[i]);
    max = Math.max(max, sum);
  }
  return max;
}

2. 斐波那契数列(优化版)

考点:递归、缓存优化、避免爆栈

javascript 复制代码
// 迭代优化(推荐,无栈溢出)
function fib(n) {
  if (n <= 2) return 1;
  let a = 1, b = 1;
  for (let i = 3; i <= n; i++) {
    [a, b] = [b, a + b];
  }
  return b;
}

六、前端工程算法(面试必手撕)

1. 防抖函数

场景:搜索框联想、输入框校验、窗口 resize、表单实时校验

核心:频繁触发重置计时,只执行最后一次

javascript 复制代码
function debounce(fn, delay = 500) {
  let timer = null;
  return function(...args) {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
      timer = null;
    }, delay);
  }
}

2. 节流函数

场景:按钮防重提交、滚动加载、轮播、高频点击

核心:固定时间内只执行一次

javascript 复制代码
function throttle(fn, delay = 1000) {
  let flag = true;
  return function(...args) {
    if (!flag) return;
    flag = false;
    setTimeout(() => {
      fn.apply(this, args);
      flag = true;
    }, delay);
  }
}

七、高频模拟手写(伪算法,前端超高频)

1. 手写 call / apply / bind

javascript 复制代码
// 手写call
Function.prototype.myCall = function(context, ...args) {
  context = context || window;
  context.fn = this;
  const res = context.fn(...args);
  delete context.fn;
  return res;
};

// 手写apply
Function.prototype.myApply = function(context, args = []) {
  context = context || window;
  context.fn = this;
  const res = context.fn(...args);
  delete context.fn;
  return res;
};

// 手写bind
Function.prototype.myBind = function(context, ...args) {
  return (...newArgs) => this.myCall(context, ...args, ...newArgs);
};

2. 手写 Promise 简易版

javascript 复制代码
class MyPromise {
  constructor(executor) {
    this.status = 'pending';
    this.value = null;
    this.reason = null;
    this.onFulfilled = [];
    this.onRejected = [];

    const resolve = (val) => {
      if (this.status !== 'pending') return;
      this.status = 'fulfilled';
      this.value = val;
      this.onFulfilled.forEach(fn => fn());
    };

    const reject = (err) => {
      if (this.status !== 'pending') return;
      this.status = 'rejected';
      this.reason = err;
      this.onRejected.forEach(fn => fn());
    };

    executor(resolve, reject);
  }

  then(resolveFn, rejectFn) {
    if (this.status === 'fulfilled') resolveFn(this.value);
    if (this.status === 'rejected') rejectFn(this.reason);
    if (this.status === 'pending') {
      this.onFulfilled.push(() => resolveFn(this.value));
      this.onRejected.push(() => rejectFn(this.reason));
    }
  }
}

八、算法面试通用答题模板(背诵)

四段式答题法

  1. 题意分析:简单说明题目要实现的效果

  2. 解题思路:遍历/递归/双指针/动态规划

  3. 核心关键点:边界处理、终止条件、去重、状态保存

  4. 业务场景:对应项目中真实使用位置

九、前端算法刷题优先级(速成顺序)

  1. 防抖、节流、深拷贝 (必问)

  2. 数组去重、扁平化、对象去重 (最高频)

  3. 字符串操作、递归树遍历 (常规笔试)

  4. 简单DP、斐波那契、最大子数组 (拔高笔试)

  5. 手写call/apply/bind/Promise (中高级必问)

相关推荐
203号居民1 小时前
LeetCode hot 100 — 138. 随机链表的复制
算法·leetcode·链表
凌晨1681 小时前
OpenCV(十一)人脸识别三大算法LBPH、EigenFace、FisherFace
人工智能·opencv·算法
明月_清风2 小时前
分治、贪心、回溯、动态规划:四种经典算法思想
后端·算法
Thomas21432 小时前
spark sort shuffer 和 hash shuffer 图解对比
算法·哈希算法
AbandonForce2 小时前
简谈线程池
开发语言·c++·算法
智购科技智能售货柜2 小时前
2026自动售货机商品掉落声学计数方案:从麦克风到频谱识别的工程实践~YH
人工智能·算法
土司大王3 小时前
LeetCode hot100——实现 Trie (前缀树)
java·算法·leetcode
水龙吟啸3 小时前
华为研发岗AI方向9.9机考题复盘&分析
人工智能·python·算法·华为
小七在进步3 小时前
类和对象(一)
java·数据结构·算法