27. 移除元素 暴力+快慢指针+相向双指针
1、暴力
解题过程
外层循环遍历nums数组,当遇到等于val的元素时,进入内层循环将后续元素均前移一位,覆盖前一个元素。
时间复杂度: O(n^2)
空间复杂度: O(1)
class Solution {
public int removeElement(int[] nums, int val) {
int n = nums.length;
for (int i = 0; i < n; i++) { // 外层循环遍历数组元素
if (nums[i] == val) {
for (int j = i + 1; j < n; j++) { //内层循环删除等于val的元素(后续元素均前移一位)
nums[j - 1] = nums[j];
}
i--; // nums[i]已被新元素覆盖,尚未判断新元素是否等于val
n--; // 未被遍历的元素均前移一位,更新遍历区间的右边界
}
}
return n;
}
}
2、快慢指针
解题过程
遍历数组,遇到等于val的元素就向前追加,覆盖前面的旧元素,最终所有有效元素都位于前面。
快指针在旧数组中找非目标元素,然后赋值给慢指针指向的新数组。
对于移除元素可以理解为,拿右边 right 所指元素填补左边 left 所指元素的坑,坑就是 left 从左到右遍历过程中遇要的需要删除的元素。
时间复杂度: O(n)
空间复杂度: O(1)
class Solution {
public int removeElement(int[] nums, int val) {
int count = 0; // 记录有效元素个数
for (int i = 0; i < nums.length; i++) {
if (nums[i] != val) nums[count++] = nums[i]; // 有效元素追加到前面
}
return count;
}
}
3、相向双指针
解题过程
left,right初始化为数组的左、右边界。若nums[left] == val,left 位置的元素需要被覆盖,直接用 right 所指元素覆盖,更新 right 指向下一个可用于(等于val、不等于val都可以)覆盖的元素;若nums[left] != val,left 位置的元素不用被覆盖 更新 left 指向下一个新元素。重复上面的过程,直至 left > right。
时间复杂度: O(n)
空间复杂度: O(1)
class Solution {
public int removeElement(int[] nums, int val) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
if (nums[left] == val) { // left所指元素需要被覆盖
nums[left] = nums[right]; // 直接将right所指元素覆盖到left位置,未判断该元素是否等于val,左边界left 不动,在下一次循环判断该元素
right--; // 更新右边界,right 指向下一个用来覆盖的元素
} else {
left++;
}
}
return left;
}
}