🔁 字符串反转 × 两数之和:前端面试高频题深度拆解(附5种反转写法 + 哈希优化)

在前端面试中, "反转字符串""两数之和" 虽看似简单,却是考察候选人 基础扎实度、代码思维、API 熟练度与算法意识 的经典组合拳。本文将带你:

  • 5 种方式实现字符串反转,对比优劣;
  • 深入剖析 两数之和的暴力解 vs 哈希优化
  • 揭秘面试官真正想考察什么;
  • 提供可直接复用的高质量代码模板。

🔄 一、字符串反转:不止一种写法

场景

输入 'hello',输出 'olleh'

💡 面试官关注点:

  • 是否熟悉数组/字符串 API(split, reverse, join
  • 能否写出清晰、健壮的循环逻辑
  • 是否理解递归思想及其风险
  • 是否会用现代语法(如扩展运算符、reduce)

✅ 方法1:经典三连(API 流)

perl 复制代码
js
编辑
function reverseStr(str) {
  return str.split('').reverse().join('');
}

const str = "hello"; 
const arr = str.split(''); 
// arr 的值会是:['h', 'e', 'l', 'l', 'o']

const arr = ['h', 'e', 'l', 'l', 'o'];
arr.reverse(); 
// 调用后,arr 本身被修改了,现在的值是:['o', 'l', 'l', 'e', 'h']

const reversedArr = ['o', 'l', 'l', 'e', 'h']; 
const reversedStr = reversedArr.join(''); 
// reversedStr 的值会是:"olleh"
  • 优点:简洁、可读性强,体现对内置方法的掌握。
  • 缺点:创建中间数组,内存开销略高(但通常可忽略)。

✅ 方法2:传统 for 循环(从后往前)

ini 复制代码
js
编辑
function reverseStr(str) {
  let reversed = '';
  for (let i = str.length - 1; i >= 0; i--) {
    reversed += str[i];
  }
  return reversed;
}
  • 优点:逻辑直观,兼容性好(ES3 起支持)。
  • 注意:字符串拼接在旧引擎中可能低效(现代 V8 已优化)。

✅ 方法3:for...of 反向拼接

csharp 复制代码
js
编辑
function reverseStr(str) {
  let reversed = '';
  for (const char of str) {
    reversed = char + reversed; // 每次把新字符放前面
  }
  return reversed;
}
  • 优点:避免索引操作,更符合"遍历字符"语义。
  • 缺点:频繁字符串拼接(虽现代 JS 引擎已优化)。

✅ 方法4:扩展运算符 + 数组方法(ES6+)

python 复制代码
js
编辑
function reverseStr(str) {
  return [...str].reverse().join('');
}

const str = "hello"; const arr = [...str]; 
// arr 的值会是:['h', 'e', 'l', 'l', 'o']
  • 优点 :比 split('') 更优雅(尤其对 emoji/Unicode 支持更好)。
  • 原理[...str] 能正确处理 UTF-16 代理对(如 '👨‍💻'.length === 5,但 [...'👨‍💻'].length === 1)。

🌰 对比:

bash 复制代码
js
编辑
'café'.split('')   // ['c','a','f','é'] ✅
'👨‍💻'.split('')    // ['👨', '‍', '💻'] ❌
[...'👨‍💻']         // ['👨‍💻'] ✅

✅ 方法5:递归实现(展示思维)

rust 复制代码
js
编辑
function reverseStr(str) {
  if (str === '') return ''; // 终止条件
  return reverseStr(str.substring(1)) + str.charAt(0);
}

const str = "hello"; 
const subStr = str.substring(1); 
// subStr 的值会是:"ello" (从索引1的 'e' 开始到末尾)

const str = "hello"; 
const firstChar = str.charAt(0); 
// firstChar 的值会是:"h" (索引0位置的字符)

1.  `reverseStr("hello")` 调用 `reverseStr("ello")`
1.  `reverseStr("ello")` 调用 `reverseStr("llo")`
1.  `reverseStr("llo")` 调用 `reverseStr("lo")`
1.  `reverseStr("lo")` 调用 `reverseStr("o")`
1.  `reverseStr("o")` 调用 `reverseStr("")`
1.  `reverseStr("")` 触发终止条件,返回 `""`
1.  `reverseStr("o")` 得到返回值 `"" + "o"`,即 `"o"`,并返回
1.  `reverseStr("lo")` 得到返回值 `"o" + "l"`,即 `"ol"`,并返回
1.  `reverseStr("llo")` 得到返回值 `"ol" + "l"`,即 `"oll"`,并返回
1.  `reverseStr("ello")` 得到返回值 `"oll" + "e"`,即 `"olle"`,并返回
1.  `reverseStr("hello")` 得到返回值 `"olle" + "h"`,即 `"olleh"`,并返回给最初的调用者
  • 优点:体现分治思想,代码简洁。

  • ⚠️ 风险

    • 爆栈:长字符串(如 >10,000 字符)会导致栈溢出;
    • 性能差 :每次 substring 都创建新字符串,时间复杂度 O(n²)。

🚫 不推荐生产使用,但面试可作为"展示递归理解"的备选。


✅ 方法6:reduce 高阶函数(函数式风格)

javascript 复制代码
js
编辑
function reverseStr(str) {
  return [...str].reduce((reversed, char) => char + reversed, '');
}
  • 优点:无状态、纯函数风格,适合函数式编程场景。
  • 缺点:可读性略低于三连 API。

📊 反转方法对比总结

方法 可读性 性能 Unicode 安全 推荐场景
split+reverse+join ⭐⭐⭐⭐ ⭐⭐⭐ ❌(部分) 快速实现
for 循环 ⭐⭐⭐ ⭐⭐⭐⭐ 兼容老环境
for...of ⭐⭐⭐⭐ ⭐⭐⭐ 现代项目
[...str] ⭐⭐⭐⭐⭐ ⭐⭐⭐ ✅✅ 首选推荐
递归 ⭐⭐ 面试展示
reduce ⭐⭐⭐ ⭐⭐ 函数式偏好

最佳实践 :日常开发优先用 [...str].reverse().join('')


🔢 二、两数之和:从暴力到哈希优化

题目 :给定整数数组 nums 和目标值 target,找出两个数的索引,使其和等于 target。假设每组输入只有一组解。

面试官真实意图:

  • 考察 时间复杂度意识
  • 是否知道 "空间换时间" 思想
  • 数据结构选择 的敏感度(Object vs Map)

❌ 解法1:暴力双重循环(O(n²))

ini 复制代码
js
编辑
function twoSum(nums, target) {
  for (let i = 0; i < nums.length; i++) {
    for (let j = i + 1; j < nums.length; j++) {
      if (nums[i] + nums[j] === target) {
        return [i, j];
      }
    }
  }
}
  • 问题:n=10⁴ 时,操作次数达 5×10⁷,明显超时。
  • 结论:仅用于兜底或教学演示。

✅ 解法2:哈希表优化(O(n))------核心思路

核心思想:

"求和变求差"

遍历时,记录每个数字及其索引;

对当前 num,检查 target - num 是否已存在。

方案A:使用普通对象(Object)

ini 复制代码
js
编辑
function twoSum(nums, target) {
  const map = {};
  for (let i = 0; i < nums.length; i++) {
    const complement = target - nums[i];
    if (map[complement] !== undefined) {
      return [map[complement], i];
    }
    map[nums[i]] = i;
  }
}
  • 风险 :键为字符串,若 nums 含非数字(如 '2'),可能混淆;
  • 性能:V8 对对象属性访问高度优化,实际很快。

方案B:使用 Map(推荐!)

ini 复制代码
js
编辑
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);
  }
}
  • 优势

    • 键类型保持原样(number 还是 number);
    • has() / get() 语义清晰;
    • 避免原型链污染(如 map.__proto__ 干扰);
    • 更符合"键值对容器"语义。

📌 面试加分点 :主动说出 "我选择 Map 而非对象,因为......"


🧠 三、面试官到底想考什么?

题目 考察维度 高分回答要点
字符串反转 - API 熟练度 - 代码风格 - 边界意识 "我会优先用 [...str] 因为它对 Unicode 更安全"
两数之和 - 算法思维 - 数据结构选择 - 复杂度分析 "暴力是 O(n²),我用哈希表降到 O(n),空间换时间"

💡 避坑提示

  • 不要一上来就写递归(除非被要求);
  • 不要说"Object 和 Map 一样"(暴露基础不牢);
  • 记得处理边界(如空字符串、无解情况)。

📌 四、总结 & 复习清单

✅ 字符串反转

  • 首选写法[...str].reverse().join('')
  • 慎用递归:有爆栈风险,性能差
  • 注意 Unicodesplit('') 对 emoji 不友好

✅ 两数之和

  • 最优解Map + 一次遍历,O(n) 时间
  • 关键转换a + b = targetb = target - a
  • 不要用 Object 当哈希表(除非明确知道数据安全)
相关推荐
前端之虎陈随易8 分钟前
2年没用Nodejs了,Bun很香
linux·前端·javascript·vue.js·typescript
好运的阿财1 小时前
OpenClaw工具拆解之host_workspace_write+host_workspace_edit
前端·javascript·人工智能·机器学习·ai编程·openclaw·openclaw工具
XiYang-DING1 小时前
JavaScript
开发语言·javascript·ecmascript
空中海2 小时前
02 React Native状态、导航、数据流与设备能力
javascript·react native·react.js
空中海3 小时前
02 状态、Hooks、副作用与数据流
开发语言·javascript·ecmascript
空中海3 小时前
04 React Native工程化、质量、发布与生态选型
javascript·react native·react.js
杨超凡4 小时前
豆包收费了?我特么自己用“意念”搓了一个!
javascript
threelab5 小时前
Three.js 咖啡杯烟雾效果 | 三维可视化 / AI 提示词
开发语言·javascript·人工智能
Heo5 小时前
14_React 中的更新队列 updateQueue
前端·javascript·面试