本题与026题相似,是关于双指针数组的问题
文内代码全部采用JAVA语言。
题目
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
测试用例
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。
个人解法
这里采用的是和026相类似的双指针法,指针 i 的方向是—>—>—>—>,从数组起始位开始,指针 j 的方向是<—<—<—<— ,从数组最末位开始。当i==j时,说明完全遍历了整个数组。
之所以产生这样的想法,是因为每当查出一个目标值,这个值就需要被数组中的其他非目标值替换。而新数组的输出值取决于新数组的长度。如果我们不能将数组尾端的非目标值前移,那么就会失去这些值。。
先从 i 开始判断,如果 i 位置的值是目标元素val,那么就需要检查j位置,如果 j 位置是非目标元素,则数组长度+1,并用 j 位置的元素取代 i 位置的元素,这样就删除了 i 位置的值,并按预定次序移动i j的位置。如果如果 j 位置也是目标元素,就向前移动 j 直到 j 位置是非目标元素。
啰里吧嗦,核心思想就是用数组尾端的非val,替换数组前端的val,每检测到一个非val,新数组长度+1。
执行用时: 9 ms, 在Remove Element的Java提交中击败了67.17% 的用户
class Solution {
public int removeElement(int[] nums, int val) {
int i=0;
int j=nums.length-1;
int length=0;
while (i<=j) {
if (nums[i]==val) {
if (nums[j]==val) {
j--;
}
else {
nums[i]=nums[j];
j--;
length++;
i++;
}
}
else {
i++;
length++;
}
}
return length;
}
}
官方解法
方法一
思路
既然问题要求我们就地删除给定值的所有元素,我们就必须用 O(1) 的额外空间来处理它。如何解决?我们可以保留两个指针 i 和 j,其中 i 是慢指针,j 是快指针。
其实和个人解法差不多,只是 i 和 j 有相同的起始位置,j 跑得快,一旦检测到非val就覆盖 i 。程序简短,不错不错。运行时间和个人解法一样,都是9ms。
算法
public int removeElement(int[] nums, int val) {
int i = 0;
for (int j = 0; j < nums.length; j++) {
if (nums[j] != val) {
nums[i] = nums[j];
i++;
}
}
return i;
}
方法二
思路
这个想法好像和个人解法差不多,都是双向来看,但是他不需要分为情况讨论,所以代码显得简洁。
避免if语句的方法就是,把尾部的数据复制到前面之后,继续检查被取代的元素位置,直到符合要求。
时间同样是9ms。
算法
public int removeElement(int[] nums, int val) {
int i = 0;
int n = nums.length;
while (i < n) {
if (nums[i] == val) {
nums[i] = nums[n - 1];
// reduce array size by one
n--;
} else {
i++;
}
}
return n;
}