leetcode_27. 移除元素
题目描述
给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素。元素的顺序可能发生改变。然后返回 nums
中与 val
不同的元素的数量。
假设 nums
中不等于 val
的元素数量为 k
,要通过此题,您需要执行以下操作:
- 更改
nums
数组,使nums
的前k
个元素包含不等于val
的元素。nums
的其余元素和nums
的大小并不重要。 - 返回
k
。
示例 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
题解-双指针法
这道题目非常经典, 经常出现在各类算法竞赛和考研题目中,
题中我们需要移除数组中所有与给定的val
相等的元素, 并且移除之后要将数组中的所有元素向左补齐, 并返回数组最后的时机长度(有效数据的数量). 这里还是使用双指针法最为便捷. 算法过程如下:
- 定义两个指针
i
,j
, 开始时都指向数组的开头 - 如果
j
指向的元素等于val
, 不进行任何操作 - 如果
j
指向的元素不等于val
, 就将j
指向的元素赋值到i
的位置, 直接覆盖i
原本的值 - 遍历结束返回
i
分析一下上面的过程, 为什么上面的操作就可以去除与val
相等的值了.
一开始i
和j
都在开头, 如果这时就遇到了val
, 那么一句我们上面的过程, 将不执行任何操作, j
直接向后移动一位, 显然, j
指针的移动只能在遇到一个不为val
的点时才会停下. 随后执行赋值的动作, 用j
指向的值不为val
的值去覆盖i
指向的值为val
的值, 然后i
向后移动一位. 这就是我们算法的核心操作.
之后, 每当j
遇到不为val
的值得时候, 都会覆盖到i
的位置.
那为什么i
总是指向等于val
的值呢, 原因就在我们每当j
遇到不为val
的值的时候, 我们的i
也会往后移, 而j
遇到等于val
的值时, i
就停下了.
再者, i
的移动次数就是不等于val
的值得数量, 即我们最后要保留的值.
那如果从最开始的位置就没有遇到等于val
的值怎么办, 不难发现, i
和j
会指向同一个位置, 然后进行赋值, 也就是自己赋值给自己. 虽然这样的操作是无效的, 冗余的, 但是这样能保证我们算法逻辑的简洁清晰. 虽然有较多的无意义操作, 我们的代码的时间复杂度仍是
O
(
1
)
O(1)
O(1).
java
class Solution {
public int removeElement(int[] nums, int val) {
int i = 0, j = 0;
while (j < nums.length) {
if (nums[j] != val) {
nums[i++] = nums[j];
}
j++;
}
return i;
}
}
c++
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int i = 0, j = 0;
while (j < nums.size()) {
if (nums[j] != val)
nums[i++] = nums[j];
j++;
}
return i;
}
};