删除元素专题
白银挑战:双指针思想及其应用
1.原地删除数组中所有元素等于val的值
解题思路:利用对撞双指针, left初始为数组下标为0的位置,right初始为数组最后一个位置;
如果当前left指向的元素等于val,且right指向的元素不等于val,那么将数组中left,right位置的值进行交换,然后left++;
如果当前left指向的元素不等于val,left++即可;
如果当前right指向的元素等于val,right--即可;
这样遍历完后,left左边的数组元素全部都是不等于val的值;
指针变换过程如图所示:
left左边的数组长度即是需要返回的长度;
代码如下:
public static int delVal(int[] arr, int val) {
if (arr.length == 0) {
return -1;
}
int left = 0;
int right = arr.length - 1;
while (left <= right) {
if (arr[left] == val && arr[right] != val) {
swap(arr, left, right);
}
if (arr[left] != val) {
left++;
}
if (arr[right] == val) {
right--;
}
}
return left;
}
// 通过异或运算来实现数组中两个位置的值的交换,前提是两个位置left,right不能相等;
// 举个例子: a=10,b=8;
// 第一步: a= a^b=10^8;
// 第二步: b= a^b=(10^8)^8; b=10;
// 第三步: a= a^b=(10^8)^10 a=8;
// a,b交换成功;
public static void swap(int[] arr, int left, int right) {
arr[left] = arr[left] ^ arr[right];
// 第二步相当于 arr[right]=(arr[left]^arr[right])^arr[right] <==> arr[right]=arr[left] ^ 0 =arr[left];
// 0和任何数异或都得该数本身;
arr[right] = arr[left] ^ arr[right];
// 第三步相当于 arr[left]=(arr[left]^arr[right](arr[right]未改变))^arr[right](arr[right]已改变) <==> arr[left]=arr[right] ^ 0;
arr[left] = arr[left] ^ arr[right];
}
2.删除有序数组中的重复项;(即每个数组元素只能在数组中出现一次)
对应 leetcode26. 删除有序数组中的重复项
解题思路:利用快慢指针
具体实现过程是让slow指针一开始指向数组 1 的位置,让fast指针可以从1位置出发,也可以从0位置出发;(我试过初始时fast=1,和fast=0,leetcode都让过)
这里为了方便,fast = 1;
如果在遍历数组的过程中,fast位置所对应的元素和 slow-1位置对应的元素不相等;那么就用fast指针位置的值覆盖掉slow指针位置所对应的值;
画图来理解:
假设给定一个数组 [1,2 ,2, 3, 3, 4, 5 ];
代码如下:
/*
* 删除有序数组中的重复元素,使数组中每个元素只出现一次
* 返回删除元素后的数组长度
* */
public static int removeDuplicates(int[] arr) {
if (arr.length == 0) {
return -1;
}
int slow = 1;
int fast = 1;
while (fast < arr.length) {
// 如果slow-1位置的值和fast位置的值不相等,那么就用arr[fast]覆盖掉arr[slow]的值;同时slow++,fast++;
if (arr[slow - 1] != arr[fast]) {
arr[slow] = arr[fast];
slow++;
}
fast++;
}
return slow;
}
3.拓展:删除有序数组中的重复项 (但只需要删除数组中重复次数大于2的值)
只要掌握了上面题目的解题方法,那么这道题目也很容易想到解题思路;依然使用快慢指针;
但是slow,fast初始值应该为2,同时arr[fast] 应该和arr[slow-2] 进行比较,也就是fast指针所应的值和slow-2位置所对应的值作比较;
如果两个数不相等,则slow++,fast++;否则fast++;
只需要保证slow位置所对应的值和从它开始往左数第二个数保持不同即可;slow位置的值可以和它前一个数相同,但一定不能和它开始往前数第二个数相同,如果是这样,slow位置的值和前面两个数同时相同,那么重复次数就会大于2;不符合题意;
代码如下:
public static int removeDuplicates(int[] arr) {
if (arr.length <= 2) {
return arr.length;
}
int slow = 2, fast = 2;
while (fast < arr.length) {
if (arr[slow - 2] != arr[fast]) {
arr[slow] = arr[fast];
slow++;
}
fast++;
}
return slow;
}
是不是和上一题很像,那我们就可以总结出,如果需要保证数组中重复出现的数可以为 k次,那么我们只要让fast指针所对应的值和slow-k所对应值相比较即可;如果不相同,slow,fast同时往后动,否则,fast单独移动;