没事儿做做算法题

185 阅读5分钟

以下是针对前端面试精选的 20 道算法题及 JavaScript 解答,涵盖高频考点和实际应用场景:


1. 两数之和

题目:给定数组和整数 target,返回和为 target 的两个元素的索引

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 [];
}

2. 有效的括号

题目:判断只包含 ()[]{} 的字符串是否有效

function isValid(s) {
  const stack = [];
  const map = { '(': ')', '[': ']', '{': '}' };
  for (let char of s) {
    if (map[char]) {
      stack.push(char);
    } else {
      if (stack.length === 0) return false;
      const top = stack.pop();
      if (map[top] !== char) return false;
    }
  }
  return stack.length === 0;
}

3. 反转链表

题目:反转单链表

function reverseList(head) {
  let prev = null;
  let curr = head;
  while (curr) {
    const next = curr.next;
    curr.next = prev;
    prev = curr;
    curr = next;
  }
  return prev;
}

4. 合并两个有序数组

题目:将 nums2 合并到 nums1 中(nums1 有足够空间)

function merge(nums1, m, nums2, n) {
  let i = m - 1, j = n - 1, k = m + n - 1;
  while (j >= 0) {
    nums1[k--] = (i >= 0 && nums1[i] > nums2[j]) 
      ? nums1[i--] 
      : nums2[j--];
  }
}

5. 二叉树的层序遍历

题目:返回二叉树节点值的层序遍历结果

function levelOrder(root) {
  if (!root) return [];
  const result = [];
  const queue = [root];
  
  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;
}

6. 最长无重复子串

题目:给定字符串,返回最长无重复字符的子串长度

function lengthOfLongestSubstring(s) {
  const map = new Map();
  let max = 0;
  let left = 0;
  
  for (let right = 0; right < s.length; right++) {
    const char = s[right];
    if (map.has(char) && map.get(char) >= left) {
      left = map.get(char) + 1;
    }
    map.set(char, right);
    max = Math.max(max, right - left + 1);
  }
  return max;
}

7. 三数之和

题目:在数组中找到所有和为 0 的三元组

function threeSum(nums) {
  nums.sort((a, b) => a - b);
  const result = [];
  
  for (let i = 0; i < nums.length - 2; i++) {
    if (i > 0 && nums[i] === nums[i - 1]) continue;
    
    let left = i + 1, right = nums.length - 1;
    while (left < right) {
      const sum = nums[i] + nums[left] + nums[right];
      if (sum === 0) {
        result.push([nums[i], nums[left], nums[right]]);
        while (left < right && nums[left] === nums[left + 1]) left++;
        while (left < right && nums[right] === nums[right - 1]) right--;
        left++;
        right--;
      } else if (sum < 0) {
        left++;
      } else {
        right--;
      }
    }
  }
  return result;
}

8. 最长回文子串

题目:返回字符串中的最长回文子串

function longestPalindrome(s) {
  let max = "";
  
  for (let i = 0; i < s.length; i++) {
    // 奇数和偶数长度回文
    for (let j = 0; j < 2; j++) {
      let left = i, right = i + j;
      while (left >= 0 && right < s.length && s[left] === s[right]) {
        left--;
        right++;
      }
      const substr = s.substring(left + 1, right);
      if (substr.length > max.length) max = substr;
    }
  }
  return max;
}

9. 爬楼梯

题目:每次爬 1 或 2 阶,到 n 阶有多少种方法

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;
}

10. 最大子数组和

题目:返回具有最大和的连续子数组的和

function maxSubArray(nums) {
  let max = nums[0];
  let current = nums[0];
  
  for (let i = 1; i < nums.length; i++) {
    current = Math.max(nums[i], current + nums[i]);
    max = Math.max(max, current);
  }
  return max;
}

11. 二叉树的最大深度

题目:计算二叉树的最大深度

function maxDepth(root) {
  if (!root) return 0;
  return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}

12. 买卖股票最佳时机

题目:只允许一次交易的最大利润

function maxProfit(prices) {
  let min = Infinity;
  let max = 0;
  
  for (let price of prices) {
    min = Math.min(min, price);
    max = Math.max(max, price - min);
  }
  return max;
}

13. 字符串相加

题目:用字符串表示的大数相加

function addStrings(num1, num2) {
  let i = num1.length - 1, j = num2.length - 1;
  let carry = 0, res = "";
  
  while (i >= 0 || j >= 0 || carry) {
    const n1 = i >= 0 ? +num1[i--] : 0;
    const n2 = j >= 0 ? +num2[j--] : 0;
    const sum = n1 + n2 + carry;
    carry = Math.floor(sum / 10);
    res = (sum % 10) + res;
  }
  return res;
}

14. 缺失的第一个正数

题目:找出未排序数组中缺失的最小正整数

function firstMissingPositive(nums) {
  const n = nums.length;
  
  // 标记非正数
  for (let i = 0; i < n; i++) {
    if (nums[i] <= 0) nums[i] = n + 1;
  }
  
  // 用索引标记存在性
  for (let i = 0; i < n; i++) {
    const num = Math.abs(nums[i]);
    if (num <= n) {
      nums[num - 1] = -Math.abs(nums[num - 1]);
    }
  }
  
  // 找第一个正数索引
  for (let i = 0; i < n; i++) {
    if (nums[i] > 0) return i + 1;
  }
  return n + 1;
}

15. 接雨水

题目:计算柱子间能接多少雨水

function trap(height) {
  let left = 0, right = height.length - 1;
  let leftMax = 0, rightMax = 0;
  let water = 0;
  
  while (left < right) {
    if (height[left] < height[right]) {
      leftMax = Math.max(leftMax, height[left]);
      water += leftMax - height[left];
      left++;
    } else {
      rightMax = Math.max(rightMax, height[right]);
      water += rightMax - height[right];
      right--;
    }
  }
  return water;
}

16. LRU 缓存

题目:设计实现 LRU 缓存

class LRUCache {
  constructor(capacity) {
    this.capacity = capacity;
    this.map = new Map();
  }

  get(key) {
    if (!this.map.has(key)) return -1;
    const val = this.map.get(key);
    this.map.delete(key);
    this.map.set(key, val);
    return val;
  }

  put(key, value) {
    if (this.map.has(key)) this.map.delete(key);
    this.map.set(key, value);
    if (this.map.size > this.capacity) {
      this.map.delete(this.map.keys().next().value);
    }
  }
}

17. 全排列

题目:返回数组所有可能的全排列

function permute(nums) {
  const result = [];
  backtrack([], nums, result);
  return result;
}

function backtrack(path, nums, result) {
  if (path.length === nums.length) {
    result.push([...path]);
    return;
  }
  
  for (let num of nums) {
    if (path.includes(num)) continue;
    path.push(num);
    backtrack(path, nums, result);
    path.pop();
  }
}

18. 岛屿数量

题目:计算二维网格中的岛屿数量

function numIslands(grid) {
  let count = 0;
  
  function dfs(i, j) {
    if (i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] === '0') return;
    grid[i][j] = '0';
    dfs(i + 1, j);
    dfs(i - 1, j);
    dfs(i, j + 1);
    dfs(i, j - 1);
  }
  
  for (let i = 0; i < grid.length; i++) {
    for (let j = 0; j < grid[0].length; j++) {
      if (grid[i][j] === '1') {
        count++;
        dfs(i, j);
      }
    }
  }
  return count;
}

19. 前 K 个高频元素

题目:返回数组中出现频率前 k 高的元素

function topKFrequent(nums, k) {
  const freqMap = new Map();
  for (const num of nums) {
    freqMap.set(num, (freqMap.get(num) || 0) + 1);
  }
  
  return [...freqMap.entries()]
    .sort((a, b) => b[1] - a[1])
    .slice(0, k)
    .map(entry => entry[0]);
}

20. 二叉搜索树中第K小元素

题目:在 BST 中查找第 k 个最小元素

function kthSmallest(root, k) {
  const stack = [];
  let curr = root;
  
  while (curr || stack.length) {
    while (curr) {
      stack.push(curr);
      curr = curr.left;
    }
    curr = stack.pop();
    if (--k === 0) return curr.val;
    curr = curr.right;
  }
}

算法题考察重点

  1. 数据结构运用:数组/链表/树/哈希表的操作
  2. 双指针技巧:滑动窗口/快慢指针/左右指针
  3. 递归思维:DFS/回溯/分治算法
  4. 动态规划:状态转移/空间优化
  5. 贪心算法:局部最优解
  6. 边界处理:空值/极端值/特殊输入
  7. 时空复杂度:分析最优解法

💡 提示:面试时先厘清题意,举例说明思路,再编码实现,最后用测试用例验证