前端常见算法面试题

509 阅读3分钟

前端工程师在算法面试中通常会遇到以下类型的题目,这些题目既考察基础算法能力,也结合前端特性。以下是常见题型及示例:


一、数组/字符串处理

  1. 两数之和

    • 题目:找出数组中两数之和等于目标值的索引
    • 解法:哈希表存储差值,时间复杂度 O(n)
    const twoSum = (nums, target) => {
      const map = new Map();
      for (let i = 0; i < nums.length; i++) {
        const complement = target - nums[i];
        if (map.has(complement)) return [map.get(complement), i];
        map.set(nums[i], i);
      }
    };
    
  2. 最长无重复子串

    • 题目:求字符串中最长不重复字符的子串长度
    • 解法:滑动窗口 + 哈希表,时间 O(n)
    const lengthOfLongestSubstring = (s) => {
      let map = new Map(), max = 0, left = 0;
      for (let right = 0; right < s.length; right++) {
        if (map.has(s[right])) left = Math.max(left, map.get(s[right]) + 1);
        map.set(s[right], right);
        max = Math.max(max, right - left + 1);
      }
      return max;
    };
    

二、DOM 树与遍历算法

  1. 深度优先遍历 DOM 树

    • 题目:实现 DFS 遍历 DOM 节点并返回所有标签名
    function dfs(node, result = []) {
      result.push(node.tagName);
      for (const child of node.children) dfs(child, result);
      return result;
    }
    
  2. 广度优先遍历 DOM 树

    • 题目:按层级输出 DOM 节点标签名
    function bfs(root) {
      const queue = [root], result = [];
      while (queue.length) {
        const node = queue.shift();
        result.push(node.tagName);
        queue.push(...node.children);
      }
      return result;
    }
    

三、排序与搜索

  1. 手写快速排序

    const quickSort = (arr) => {
      if (arr.length <= 1) return arr;
      const pivot = arr.pop();
      const left = arr.filter(x => x <= pivot);
      const right = arr.filter(x => x > pivot);
      return [...quickSort(left), pivot, ...quickSort(right)];
    };
    
  2. 实现数组的扁平化与去重

    • 输入:[1, [2, [3, 4]], 2] → 输出:[1, 2, 3, 4]
    const flattenAndUnique = (arr) => 
      [...new Set(arr.flat(Infinity))].sort((a,b) => a - b);
    

四、链表操作

  1. 反转链表

    function reverseList(head) {
      let prev = null, curr = head;
      while (curr) {
        const next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
      }
      return prev;
    }
    
  2. 判断环形链表(快慢指针)

    const hasCycle = (head) => {
      let slow = head, fast = head;
      while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
        if (slow === fast) return true;
      }
      return false;
    };
    

五、动态规划与贪心

  1. 爬楼梯问题

    • 题目:每次爬 1 或 2 阶,到第 n 阶有多少种方法?
    const climbStairs = (n) => {
      let [a, b] = [1, 1];
      for (let i = 2; i <= n; i++) [a, b] = [b, a + b];
      return b;
    };
    
  2. 买卖股票的最佳时机

    • 题目:一次交易的最大利润
    const maxProfit = (prices) => {
      let min = Infinity, max = 0;
      for (const price of prices) {
        min = Math.min(min, price);
        max = Math.max(max, price - min);
      }
      return max;
    };
    

六、设计类问题

  1. 实现 Promise.all

    Promise.myAll = (promises) => {
      return new Promise((resolve, reject) => {
        let results = [], count = 0;
        promises.forEach((p, i) => {
          p.then(res => {
            results[i] = res;
            if (++count === promises.length) resolve(results);
          }).catch(reject);
        });
      });
    };
    
  2. LRU 缓存机制

    • 结合 Map 和双向链表实现 O(1) 的 get/put 操作。

七、其他高频题

  • 有效的括号(栈的应用)
  • 合并两个有序链表
  • 二叉树的层序遍历
  • 实现防抖(debounce)与节流(throttle)
  • 千分位格式化数字(正则或数值处理)

考察重点

  1. 复杂度分析:时间/空间复杂度的计算与优化思路
  2. 代码简洁性:能否用 ES6+ 语法简化代码(如 MapSet、解构赋值)
  3. 边界处理:空输入、极端值、特殊数据结构的处理
  4. 前端结合点:如用算法优化 DOM 操作、处理虚拟 DOM 差异等

建议结合 LeetCode(标签:前端、算法)和《剑指 Offer》进行针对性练习,重点关注高频题目的多种解法。