数组与字符串进阶操作
本节目标
- 掌握双指针、原地修改、区间处理三大数组技巧
- 掌握字符串常用操作与字符频次统计
- 会分析"删除元素 / 合并区间"类题目的边界
技巧一:双指针(快慢 / 左右)——原地去重、移除元素的标准姿势。
// 运行环境:Node.js 14+
// 保存为 dsa-l4.js,执行:node dsa-l4.js
// 快慢指针:原地移除目标值,返回新长度
function removeElement(nums, val) {
let slow = 0
for (let fast = 0; fast < nums.length; fast++) {
if (nums[fast] !== val) nums[slow++] = nums[fast] // 不等于 val 才保留
}
return slow // [0, slow) 是保留区
}
// 左右对撞:反转、两数之和有序版、回文判断
function reverseArr(arr) {
let l = 0, r = arr.length - 1
while (l < r) [arr[l++], arr[r--]] = [arr[r], arr[l]]
}
// 调用示例
const a = [3, 2, 2, 3]
console.log(removeElement(a, 3), a) // 2 [2, 2, 3, 3](前 2 个有效)
const b = [1, 2, 3, 4]
reverseArr(b); console.log(b) // [4, 3, 2, 1]技巧二:覆盖式原地修改——不删除元素,用写入指针覆盖即可。
技巧三:区间合并(面试高频):
// 合并重叠区间:先按起点排序,再逐个合并
function merge(intervals) {
if (!intervals.length) return []
intervals.sort((a, b) => a[0] - b[0])
const res = [intervals[0]]
for (let i = 1; i < intervals.length; i++) {
const last = res[res.length - 1]
const cur = intervals[i]
if (cur[0] <= last[1]) last[1] = Math.max(last[1], cur[1]) // 重叠则扩展右边界
else res.push(cur)
}
return res
}
console.log(merge([[1, 3], [2, 6], [8, 10], [15, 18]])) // [[1,6],[8,10],[15,18]]字符串常用操作:
// 字符频次统计(字母异位词的判断手段)
function countChars(s) {
const cnt = new Array(26).fill(0)
for (const ch of s) cnt[ch.charCodeAt(0) - 97]++
return cnt.join(',') // 作为"频次指纹"
}
console.log(countChars('aab') === countChars('aba')) // true(异位词)边界意识:数组题先问"是否允许修改原数组 / 额外空间 / 元素范围";写代码前列 2-3 个用例(空数组、单元素、重复元素)。
名词解释
- 双指针(Two Pointers):用两个索引在数组上"协同游走"的技巧。常见形态:快慢指针(一个找、一个写)、左右对撞(一头一尾向中间夹)。本质是用一次遍历完成本需两次的工作,把 O(n²) 降到 O(n)。
- 区间(Interval):用
[起点, 终点]表示的一段范围。区间合并是"把重叠段拼成大段"的贪心过程,核心是先排序再贪心扩展。 - 字符频次指纹:把字符串各字符出现次数拼成一个定长序列,两个串指纹相同则互为异位词(字母组成一样、顺序不同)。
课后练习
- 原地移除数组重复项(有序数组,O(1) 空间)怎么写?
- 答案:快慢指针,slow 指"已去重区末尾",
if(nums[fast]!==nums[slow]) nums[++slow]=nums[fast];初始 slow=0。返回 slow+1。
- 答案:快慢指针,slow 指"已去重区末尾",
- 写
merge(intervals)时如果输入是空数组会怎样?- 答案:上面代码开头
if(!intervals.length) return []直接返回空,避免intervals[0]取 undefined 报错。边界用例要覆盖。
- 答案:上面代码开头
总结
数组和字符串是算法题的"主战场",而双指针是这片战场最通用的武器。它解决的核心矛盾是:很多题目表面上要求"删除/去重/反转",但真正的难点是"怎么在不额外开数组的前提下完成"。快慢指针用"写指针覆盖"把删除变成 O(1) 空间,左右对撞把反转/回文/两数之和变成一次遍历。我常跟学员说:看到"有序数组""去重""原地"这几个词,脑子里就该立刻冒出双指针。区间合并则教会我们一个更重要的思维——先排序再贪心,往往能把混乱的 O(n²) 暴力化简成 O(n log n)。记住,数组题永远先问边界(空?单元素?重复?),列用例再动手,比写完调试省十倍时间。