📖 双指针专题怎么用?
双指针是面试出场率最高的技巧——两个指针从两端、或快慢不同速度遍历,把 O(n²) 降到 O(n)。
本专题包含 4 道题,都是外企面试高频真题。每道题按统一结构讲解:
- 题目描述 — 用人话翻译,不用读三遍才懂
- 生活类比 — 把抽象问题变成熟悉的生活场景
- 暴力思路 → 优化思路 — 面试官想看到的思考过程
- IDE 代码 — 带注释的 JS 实现 + 行号
- 复杂度分析 — 时间/空间,面试必问
- 面试加分项 — 能说出来的额外思考
复习建议:先看生活类比理解问题本质,再看思路建立解题框架,最后手写代码 3 遍形成肌肉记忆。面试时先口述思路,确认无误后再写代码。
1回文检测 (Valid Palindrome) Easy
题目(人话版)
判断一个字符串是不是"回文"——正着读和倒着读一样。比如 "racecar" 正着读和倒着读都是 "racecar",是回文。"hello" 倒过来是 "olleh",不是。
💡 生活类比:回文就像镜像对称——你拿一面镜子放在字符串中间,左右两边完全对称。就像一些城市的设计:左边有个公园,右边也有个一模一样的公园。
方法一:翻转比较法(最直觉)
把字符串翻转过来,和原字符串比较——一样就是回文。一行代码搞定。
palindrome.js
function isPalindrome(s) {
return s === s.split('').reverse().join('');
}
// "racecar" → split → ['r','a','c','e','c','a','r']
// → reverse → ['r','a','c','e','c','a','r'] (不变!)
// → join → "racecar" ✓
简单粗暴,但创建了新数组,空间 O(n)。
方法二:双指针法(面试标准答案)
双指针法:左右两个指针从两端向中间走,每次比较指向的字符是否相同
palindrome-pointers.js
function isPalindrome(s) {
let left = 0;
let right = s.length - 1;
while (left < right) {
if (s[left] !== s[right]) {
return false; // 不一样就不是回文
}
left++; // 左指针往右走
right--; // 右指针往左走
}
return true; // 全部比完了,都一样
}
| 方法 | 时间 | 空间 | 特点 |
| 翻转比较 | O(n) | O(n) | 一行代码,但用了额外空间 |
| 双指针 | O(n) | O(1) | ✅ 不用额外空间,面试首选 |
面试变体:面试官会说"如果字符串里有空格和标点呢?比如 'A man, a plan, a canal: Panama'"。解法:先过滤掉非字母数字、转小写,再用双指针。
2反转字符串 (Reverse String) Easy
题目(人话版)
把 ['h','e','l','l','o'] 反转成 ['o','l','l','e','h']。要求原地修改,不能用额外数组。
💡 生活类比:就像排队时让队伍掉个头——最左边的人和最右边的人交换位置,然后第二左和第二右交换……一直交换到中间。
思路:双指针交换
双指针从两端向中间走,每次交换两个指针指向的元素
reverseString.js
function reverseString(s) {
let left = 0;
let right = s.length - 1;
while (left < right) {
// ES6 解构赋值交换两个元素(一行搞定!)
[s[left], s[right]] = [s[right], s[left]];
left++;
right--;
}
}
// 其他一行写法(面试官追问"你知道几种?"):
// s.reverse() ← 最简单,但面试要你说原理
时间 O(n),空间 O(1)。原地修改,不需要额外数组。
面试加分项:主动说"除了双指针,JS 还有 s.reverse() 和 s.split('').reverse().join('') 两种写法,但 reverse() 会修改原数组,面试要求原地修改时正好用它。双指针的好处是可以在任何语言通用——C/Java/Python 都能写。"
3合并两个有序数组 (Merge Sorted Arrays) Easy
题目(人话版)
给你两个已经排好序的数组 [1,3,5] 和 [2,4,6],合并成一个有序数组 [1,2,3,4,5,6]。
💡 生活类比:就像两列已经按身高排好的队伍,要合成一列还是按身高排。你不需要把所有人混在一起重新排——只需要看两队最前面的人谁矮,谁矮谁先出来站到新队伍里。这就是"归并"思想。
思路:双指针
比较两队最前面的元素,小的先进结果数组
mergeSorted.js
function merge(nums1, nums2) {
const result = [];
let i = 0, j = 0;
// 两队都还有人的时候,比谁的小
while (i < nums1.length && j < nums2.length) {
if (nums1[i] <= nums2[j]) {
result.push(nums1[i++]);
} else {
result.push(nums2[j++]);
}
}
// 其中一队走完了,把另一队剩余的都加进去
while (i < nums1.length) result.push(nums1[i++]);
while (j < nums2.length) result.push(nums2[j++]);
return result;
}
时间 O(n+m),空间 O(n+m)。两个数组各走一遍,不会回头。
别忘收尾!while 循环结束后,一定有一队还剩人没进结果数组。两个 while 收尾就是把剩下的全推进去。漏掉这个是初学者最常见的 bug。
4移动零 (Move Zeroes) Easy
题目(人话版)
把数组里所有的 0 移到末尾,其他数字保持原来的顺序。[0,1,0,3,12] → [1,3,12,0,0]。要求原地修改。
💡 生活类比:想象一排人站着,其中一些人没来(空位=0)。你要让所有来了的人往前挪,空位都留到最后。快慢指针就像:一个人负责"往后找下一个来了的人"(快指针),另一个人负责"站在下一个该填的位置等着"(慢指针)。
思路:快慢双指针
慢指针指向"下一个非零数应该放的位置",快指针负责扫描整个数组。
moveZeroes.js
function moveZeroes(nums) {
let slow = 0; // 下一个非零数该放的位置
for (let fast = 0; fast < nums.length; fast++) {
if (nums[fast] !== 0) {
// 快指针遇到非零,和慢指针交换
[nums[slow], nums[fast]] = [nums[fast], nums[slow]];
slow++; // 慢指针往前走一格
}
}
}
// [0,1,0,3,12] → [1,0,0,3,12] → [1,3,0,0,12] → [1,3,12,0,0]
时间 O(n),空间 O(1)。一次遍历搞定。
面试加分:主动说"也可以先统计 0 的个数,然后把非零数覆盖到前面,最后补 0。但双指针交换法更优雅,一次遍历就完成。"
📋 双指针专题速查表
| # | 题目 | 难度 |
| 1 | 回文检测 (Valid Palindrome) | Easy |
| 2 | 反转字符串 (Reverse String) | Easy |
| 3 | 合并两个有序数组 (Merge Sorted Arrays) | Easy |
| 4 | 移动零 (Move Zeroes) | Easy |
考点核心:双指针专题的核心就是"用对工具,降复杂度"。
面试时拿到题先想:"这道题能用双指针吗?"
如果能,先说思路再写代码,面试官就知道你的分析能力。
🔗 其他考点专题
→ 哈希表专题:两数之和 (Two Sum), 有效的字母异位词 (Valid Anagram), 数组去重/判断有重复 (Contains Duplicate), 第一个唯一字符 (First Unique Character)
→ 动态规划专题:斐波那契数列 (Fibonacci), 爬楼梯 (Climbing Stairs), 最大子序和 (Max Subarray)
→ 位运算专题:只出现一次的数字 (Single Number), 缺失数字 (Missing Number)
→ 栈与滑动窗口专题:有效括号 (Valid Parentheses), 最长不重复子串 (Longest Substring)
→ 综合技巧专题:FizzBuzz, 买卖股票的最佳时机 (Best Time to Buy/Sell Stock), 多数元素 (Majority Element), 旋转数组 (Rotate Array), 二分查找 (Binary Search)
← 返回图解算法与模式