前言:重生之我在地球用JS写算法题
1. 移动零:如何优雅地“整理”数组?
题目要求:
给定一个数组 nums,将所有 0 移动到数组末尾,同时保持非零元素的相对顺序。
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
解法思路:双指针(快慢指针)
- 慢指针
i:指向下一个非零元素应该放置的位置 - 快指针
j:遍历整个数组
var moveZeroes = function(nums) {
let i = 0; // 慢指针
for (let j = 0; j < nums.length; j++) {
if (nums[j] !== 0) {
nums[i] = nums[j];
i++;
}
}
// 剩余位置填充 0
nums.fill(0, i);
};
核心技巧:
- 使用
nums.fill(0, i)从i开始填充0,ES6 新特性,简洁高效 - 时间复杂度:O(n),空间复杂度:O(1) —— 原地操作,不额外占用内存
类比:就像整理书架,先把有用的书按顺序摆好,再把空位用废纸填满。
2. 盛最多水的容器:双指针 + 贪心的完美结合
题目要求:
给定数组 height,每个元素代表一根竖线的高度,找出两条线,使得它们与 x 轴构成的容器能装最多的水。
解法思路:双指针从两端向中间收缩
- 面积 = min(左高, 右高) × 距离
- 贪心策略:每次移动较短的一边,因为面积由短板决定,移动长边不会增加面积
var maxArea = function(height) {
let left = 0, right = height.length - 1;
let maxArea = 0;
while (left < right) {
const width = right - left;
const minHeight = Math.min(height[left], height[right]);
const area = minHeight * width;
maxArea = Math.max(maxArea, area);
if (height[left] <= height[right]) {
left++;
} else {
right--;
}
}
return maxArea;
};
为什么贪心是对的?
- 移动长边:宽度减小,高度不会增加(仍由短板决定)→ 面积可能变小
- 移动短边:虽然宽度减小,但高度可能增加 → 有机会找到更大的面积
这是双指针贪心的经典案例,面试高频!
3. 有效的括号:栈的教科书级应用
题目要求:
判断一个只包含 '(', ')', '{', '}', '[', ']' 的字符串是否有效。
输入: "()[]{}" → true
输入: "([)]" → false
解法思路:栈(Stack)
- 遇到左括号:入栈
- 遇到右括号:检查栈顶是否匹配的左括号
- 匹配:出栈
- 不匹配:返回
false
- 最后检查栈是否为空
var isValid = function(s) {
const stack = [];
const map = {
')': '(',
'}': '{',
']': '['
};
for (let char of s) {
if (char === '(' || char === '{' || char === '[') {
stack.push(char); // 左括号入栈
} else {
if (stack.pop() !== map[char]) {
return false; // 不匹配
}
}
}
return stack.length === 0; // 栈为空说明全部匹配
};
关键点:
stack.pop()返回栈顶元素并移除- 如果栈为空时
pop()返回undefined,自动判断为不匹配 - 时间复杂度 O(n),空间复杂度 O(n)
栈是处理“嵌套结构”和“匹配问题”的利器,如 HTML 标签匹配、表达式求值等。
总结:三道题,三种核心算法思想
| 题目 | 核心思想 | 应用场景 |
|---|---|---|
| 移动零 | 双指针 + 原地操作 | 数组整理、去重 |
| 盛最多水的容器 | 双指针 + 贪心 | 最大值问题、区间优化 |
| 有效的括号 | 栈 | 匹配、嵌套、语法分析 |