Skip to content

算法面试题精选

汇总前端面试高频算法题:排序、数组、字符串、链表、二叉树、动态规划等。

排序算法

1. 手写快速排序
javascript
function quickSort(arr) {
    if (arr.length <= 1) return arr;
    const pivot = arr[Math.floor(arr.length / 2)];
    const left = arr.filter(x => x < pivot);
    const mid = arr.filter(x => x === pivot);
    const right = arr.filter(x => x > pivot);
    return [...quickSort(left), ...mid, ...quickSort(right)];
}

时间复杂度:平均 O(n log n),最坏 O(n²)(已排序数组)


2. 常见排序算法复杂度对比
算法平均时间最坏时间空间稳定性
冒泡排序O(n²)O(n²)O(1)稳定
选择排序O(n²)O(n²)O(1)不稳定
插入排序O(n²)O(n²)O(1)稳定
归并排序O(n log n)O(n log n)O(n)稳定
快速排序O(n log n)O(n²)O(log n)不稳定
堆排序O(n log n)O(n log n)O(1)不稳定

数组操作

3. 数组去重(多种方法)
javascript
const arr = [1, 2, 2, 3, 3, 4];

// 方法1:Set(最简洁)
[...new Set(arr)];

// 方法2:filter + indexOf
arr.filter((item, index) => arr.indexOf(item) === index);

// 方法3:reduce
arr.reduce((acc, cur) => acc.includes(cur) ? acc : [...acc, cur], []);

// 对象数组按属性去重
const unique = (arr, key) => {
    const seen = new Set();
    return arr.filter(item => {
        const val = item[key];
        return seen.has(val) ? false : seen.add(val);
    });
};

4. 数组扁平化
javascript
const arr = [1, [2, [3, [4]]]];

// 方法1:原生 flat
arr.flat(Infinity);

// 方法2:递归
function flatten(arr) {
    return arr.reduce((acc, item) =>
        Array.isArray(item) ? [...acc, ...flatten(item)] : [...acc, item], []);
}

// 方法3:栈(迭代,性能好)
function flatten(arr) {
    const stack = [...arr], result = [];
    while (stack.length) {
        const item = stack.pop();
        Array.isArray(item) ? stack.push(...item) : result.unshift(item);
    }
    return result;
}

5. 两数之和(LeetCode 1)
javascript
function 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);
    }
    return [];
}
// 时间 O(n),空间 O(n)

6. 最大子数组和(LeetCode 53,Kadane 算法)
javascript
function maxSubArray(nums) {
    let maxSum = nums[0], currentSum = nums[0];
    for (let i = 1; i < nums.length; i++) {
        currentSum = Math.max(nums[i], currentSum + nums[i]);
        maxSum = Math.max(maxSum, currentSum);
    }
    return maxSum;
}
// 时间 O(n),空间 O(1)

字符串操作

7. 判断回文串
javascript
function isPalindrome(s) {
    s = s.toLowerCase().replace(/[^a-z0-9]/g, '');
    let left = 0, right = s.length - 1;
    while (left < right) {
        if (s[left] !== s[right]) return false;
        left++; right--;
    }
    return true;
}

8. 最长不重复子串(LeetCode 3)
javascript
function lengthOfLongestSubstring(s) {
    const map = new Map(); // 字符 → 最新索引
    let maxLen = 0, left = 0;
    for (let right = 0; right < s.length; right++) {
        if (map.has(s[right]) && map.get(s[right]) >= left) {
            left = map.get(s[right]) + 1; // 移动左指针
        }
        map.set(s[right], right);
        maxLen = Math.max(maxLen, right - left + 1);
    }
    return maxLen;
}
// 滑动窗口,时间 O(n)

链表

9. 反转链表(LeetCode 206)
javascript
function reverseList(head) {
    let prev = null, curr = head;
    while (curr) {
        const next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}
// 时间 O(n),空间 O(1)

10. 判断链表是否有环(LeetCode 141,快慢指针)
javascript
function 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;
}

二叉树

11. 二叉树的三种遍历(迭代版)
javascript
// 前序遍历(根左右)
function preorder(root) {
    if (!root) return [];
    const stack = [root], result = [];
    while (stack.length) {
        const node = stack.pop();
        result.push(node.val);
        if (node.right) stack.push(node.right);
        if (node.left) stack.push(node.left);
    }
    return result;
}

// 层序遍历(BFS)
function levelOrder(root) {
    if (!root) return [];
    const queue = [root], result = [];
    while (queue.length) {
        const level = [];
        const size = queue.length;
        for (let i = 0; i < size; i++) {
            const node = queue.shift();
            level.push(node.val);
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }
        result.push(level);
    }
    return result;
}

12. 二叉树最大深度(LeetCode 104)
javascript
function maxDepth(root) {
    if (!root) return 0;
    return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}

动态规划

13. 爬楼梯(LeetCode 70)
javascript
function climbStairs(n) {
    if (n <= 2) return n;
    let a = 1, b = 2;
    for (let i = 3; i <= n; i++) {
        [a, b] = [b, a + b];
    }
    return b;
}
// 斐波那契数列变体,时间 O(n),空间 O(1)

14. 背包问题(0-1 背包)
javascript
function knapsack(weights, values, capacity) {
    const n = weights.length;
    const dp = new Array(capacity + 1).fill(0);
    for (let i = 0; i < n; i++) {
        // 从后往前遍历,防止重复选取
        for (let j = capacity; j >= weights[i]; j--) {
            dp[j] = Math.max(dp[j], dp[j - weights[i]] + values[i]);
        }
    }
    return dp[capacity];
}

前端特色算法

15. 实现 LRU 缓存(LeetCode 146)
javascript
class LRUCache {
    constructor(capacity) {
        this.capacity = capacity;
        this.cache = new Map(); // Map 保持插入顺序
    }

    get(key) {
        if (!this.cache.has(key)) return -1;
        const val = this.cache.get(key);
        this.cache.delete(key);
        this.cache.set(key, val); // 移到末尾(最近使用)
        return val;
    }

    put(key, value) {
        if (this.cache.has(key)) this.cache.delete(key);
        else if (this.cache.size >= this.capacity) {
            this.cache.delete(this.cache.keys().next().value); // 删除最久未用(头部)
        }
        this.cache.set(key, value);
    }
}

16. 深度优先搜索(DFS)和广度优先搜索(BFS)
javascript
// DFS:递归或栈,适合找路径、判断连通性
function dfs(graph, start, visited = new Set()) {
    visited.add(start);
    for (const neighbor of graph[start] || []) {
        if (!visited.has(neighbor)) dfs(graph, neighbor, visited);
    }
    return visited;
}

// BFS:队列,适合找最短路径
function bfs(graph, start) {
    const queue = [start], visited = new Set([start]);
    while (queue.length) {
        const node = queue.shift();
        for (const neighbor of graph[node] || []) {
            if (!visited.has(neighbor)) {
                visited.add(neighbor);
                queue.push(neighbor);
            }
        }
    }
    return visited;
}