009-移除元素 (Remove Element)
LeetCode 第27题 - 移除元素的详细题解
题目描述
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2,_,_]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3,_,_,_]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。解题思路
使用双指针法,一个指针 slow 指向当前要保留的元素位置,另一个指针 fast 遍历数组。当 fast 指向的元素不等于 val 时,将其移动到 slow 位置,然后 slow 向前移动。
JavaScript 解决方案
/**
* @param {number[]} nums
* @param {number} val
* @return {number}
*/
const removeElement = (nums, val) => {
let slow = 0;
for (let fast = 0; fast < nums.length; fast++) {
// 如果当前元素不等于要移除的值,则保留
if (nums[fast] !== val) {
nums[slow] = nums[fast];
slow++;
}
}
return slow;
};复杂度分析
- 时间复杂度:O(n),其中 n 是数组长度
- 空间复杂度:O(1),只使用了常数额外空间
测试用例
// 测试函数
const testRemoveElement = () => {
const testCases = [
{
nums: [3, 2, 2, 3],
val: 3,
expected: 2,
expectedArray: [2, 2]
},
{
nums: [0, 1, 2, 2, 3, 0, 4, 2],
val: 2,
expected: 5,
expectedArray: [0, 1, 3, 0, 4]
},
{
nums: [1, 2, 3, 4],
val: 5,
expected: 4,
expectedArray: [1, 2, 3, 4]
},
{
nums: [1, 1, 1, 1],
val: 1,
expected: 0,
expectedArray: []
},
{
nums: [],
val: 1,
expected: 0,
expectedArray: []
},
{
nums: [1],
val: 1,
expected: 0,
expectedArray: []
},
{
nums: [1],
val: 2,
expected: 1,
expectedArray: [1]
}
];
testCases.forEach((testCase, index) => {
const nums = [...testCase.nums]; // 复制数组避免修改原数组
const result = removeElement(nums, testCase.val);
const resultArray = nums.slice(0, result);
const passed = result === testCase.expected &&
JSON.stringify(resultArray.sort()) === JSON.stringify(testCase.expectedArray.sort());
console.log(`测试用例 ${index + 1}: ${passed ? '通过' : '失败'}`);
console.log(`输入: nums = [${testCase.nums}], val = ${testCase.val}`);
console.log(`期望: ${testCase.expected}, nums = [${testCase.expectedArray}]`);
console.log(`实际: ${result}, nums = [${resultArray}]\n`);
});
};
// 运行测试
testRemoveElement();关键点总结
- 双指针法:使用快慢指针来原地修改数组
- 原地操作:不创建新数组,直接在原数组上操作
- 元素顺序:题目允许改变元素顺序
- 边界处理:空数组、单个元素、全部移除等特殊情况
- 返回值:返回移除后数组的新长度
总结
这道题考察了以下重点:
- 双指针技巧:理解快慢指针在数组操作中的应用
- 原地算法:如何在不使用额外空间的情况下修改数组
- 数组遍历:高效地遍历和处理数组元素
- 边界处理:对各种边界情况的考虑
双指针法是最优解,时间复杂度为 O(n),空间复杂度为 O(1)。在实际面试中,建议先解释双指针的思路,然后实现算法。