力扣解题-27. 移除元素
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。
假设 nums 中不等于 val 的元素数量为 k,要通过此题,您需要执行以下操作:
- 更改 nums 数组,使 nums 的前 k 个元素包含不等于 val 的元素。nums 的其余元素和 nums 的大小并不重要。
- 返回 k。
用户评测: 评测机将使用以下代码测试您的解决方案:
int[] nums = [...]; // 输入数组
int val = ...; // 要移除的值
int[] expectedNums = [...]; // 长度正确的预期答案。
// 它以不等于 val 的值排序。
int k = removeElement(nums, val); // 调用你的实现
assert k == expectedNums.length;
sort(nums, 0, k); // 排序 nums 的前 k 个元素
for (int i = 0; i < actualLength; i++) {
assert nums[i] == expectedNums[i];
}
如果所有的断言都通过,你的解决方案将会 通过。
示例 1: 输入:nums = [3,2,2,3], val = 3 输出:2, nums = [2,2,,] 解释:你的函数应该返回 k = 2, 并且 nums 中的前两个元素均为 2。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
示例 2: 输入:nums = [0,1,2,2,3,0,4,2], val = 2 输出:5, nums = [0,1,4,0,3,,,_] 解释:你的函数应该返回 k = 5,并且 nums 中的前五个元素为 0,0,1,3,4。 注意这五个元素可以任意顺序返回。 你在返回的 k 个元素之外留下了什么并不重要(因此它们并不计入评测)。
提示:
0 <= nums.length <= 100
0 <= nums[i] <= 50
0 <= val <= 100
第一次解答
解题思路
核心方法:快慢双指针(读写指针)原地覆盖,通过一个“读指针”遍历数组,一个“写指针”记录非目标值的写入位置,仅一次遍历完成原地移除,时间复杂度O(n)、空间复杂度O(1),是本题的最优解法。
核心原理铺垫
题目要求“原地移除”,且仅关注前k个非val元素(后续元素无要求),因此无需创建新数组,只需将非val元素依次覆盖到数组前半部分:
- 读指针(read):负责遍历整个数组,检查每个元素是否为val;
- 写指针(write):负责记录“下一个非val元素应该写入的位置”,初始为0(从数组开头开始写入)。
具体步骤
- 初始化写指针:
int write = 0,表示第一个非val元素将写入nums[0]的位置; - 读指针遍历数组:
for (int read = 0; read < nums.length; read++),逐个检查数组元素:- 若当前元素
nums[read] != val(非目标值,需要保留):- 将该元素写入写指针位置:
nums[write] = nums[read]; - 写指针右移一位(
write++),为下一个非val元素预留位置;
- 将该元素写入写指针位置:
- 若当前元素
nums[read] == val(目标值,需要移除):不做任何操作,读指针继续前进,写指针保持不动;
- 若当前元素
- 返回结果:遍历完成后,
write的值即为数组中非val元素的数量k(因为每写入一个非val元素,write就加1,最终write的数值等于有效元素的个数)。
核心优化逻辑说明
- 时间复杂度最优:仅需一次遍历数组(O(n)),每个元素仅被读指针访问一次,无嵌套循环或冗余计算,因此耗时0ms击败100%用户;
- 空间复杂度极致:全程仅使用两个指针变量(write、read),无额外数组/集合创建,完全满足“原地操作”的要求;
- 逻辑适配题目评测规则:
- 无需关心前k个元素的顺序(题目允许元素顺序改变),也无需处理k之后的元素(评测机仅校验前k个元素);
- 写指针的移动逻辑天然保证前k个位置存储的是所有非val元素,完美契合题目对数组修改的要求;
- 内存表现说明:内存消耗43MB仅击败22.89%用户,并非逻辑问题,而是评测机环境差异(如JVM内存分配、其他进程占用等),该解法的内存开销已达到理论最优(O(1))。
执行耗时:0 ms,击败了100.00% 的Java用户 内存消耗:43 MB,击败了22.89% 的Java用户
public int removeElement(int[] nums, int val) {
int write=0;
for (int read = 0; read < nums.length; read++) {
if(nums[read]!=val){
nums[write]=nums[read];
write++;
}
}
return write;
}
总结
- 该解法的核心是快慢双指针的分工协作:读指针负责“扫描”,写指针负责“留存有效元素”,仅一次遍历完成原地修改,是数组原地操作类问题的经典技巧;
- 无需额外空间、无需复杂逻辑,仅通过简单的条件判断和指针移动,就满足了题目“原地移除”和“返回有效元素数量”的核心要求;
- 时间复杂度O(n)、空间复杂度O(1),是本题的理论最优解,完全适配题目数据规模(nums.length ≤ 100)。