一.删除有序数组中的重复项(简单)
1.题目
2.解题思路—双指针
首先注意数组是有序的,那么重复的元素一定会相邻。要求删除重复元素,实际上就是将不重复的元素移到数组左侧。
考虑用两个指针,一个在前记作p,一个在后记作q,算法流程:
比较p和q是否相等,如果相等,将q位置的元素复制到p+1位置上,p后移一位,q后移1位。
重复上述过程,直到q等于数组长度
返回p+1,即为新数组长度
3.图解
4.参考代码
int removeDuplicates(int* nums, int numsSize) {
if(numsSize == 0)
{
return 0;
}
int p = 0;
int q = 1;
while(q < numsSize)
{
if(nums[p] != nums[q])
{
nums[p + 1] = nums[q];
p++;
}
q++;
}
return p + 1;
}
二.移除元素
1.题目
2.解法一:双指针
(1)解题思路:
由于题目要求删除数组中等于val的元素,因此输出数组的长度一定小于等于输入数组的长度,我们可以把输出的数组直接写在输入数组上。利用双指针:右指针 right 指向当前要处理的元素,左指针 left 指向下一个将要被赋值的位置。
● 如果右指针指向的元素不等于 val,它一定是输出数组的一个元素,我们就将右指针指向的元素复制到左指针的位置,然后将左指针 left 指向下一个将要被赋值的元素。
● 如果右指针指向的元素等于val, 它不能在输出数组中,此时,左指针不动,右指针右移一位。
效果:区间[0,left)中的元素都不等于val。当右指针遍历完数组,left的值就是输出数组的长度。
(2)图解
(3)参考代码
int removeElement(int* nums, int numsSize, int val) {
int left = 0;
for(int right = 0;right < numsSize;right++)
{
if(nums[right] != val)
{
nums[left] = nums[right];
++left;
}
}
return left;
}
3.解法二:双指针优化
(1)解题思路
如果要移除的元素恰好在数组的开头,例如序列[ 1 , 2 , 3 , 4 , 5 ],当val为1时,如果利用上面的方法就需要将每一个元素都左移一位。注意到题目上说:元素的顺序可以改变。实际上我们可以将最后一个元素移动到序列开头,取代1,得到序列[5 , 2 , 3 , 4 ],同样,满足题目要求。
实现:利用双指针,初始时两个指针分别位于数组的首尾,向中间移动遍历该序列。
● 如果左指针left指向的元素等于val,就将右指针right指向的元素复制到左指针left的位置,然后右指针right左移一位。如果赋值过来的元素恰好等于val,可以继续把右指针right指向的元素值赋值过来(左指针left指向的等于val的元素的位置继续被覆盖),直到左指针指向元素的值不等于val为止。
● 如果左指针left指向的元素不等于val,就将左指针右移一位。
当左右指针重合的时候,左右指针遍历完数组中的所有元素。这样的方法两个指针在最坏的情况下,合起来只遍历了数组一遍。与方法一不同的是,方法二避免了需要保留的元素的重复复制操作。
(2)图解
(3)参考代码
int removeElement(int* nums, int numsSize, int val) {
int right = numsSize;
int left = 0;
while(left < right)
{
if(nums[left] == val)
{
nums[left] = nums[right-1];
right--;
}
else
{
left++;
}
}
return left;
}
三.删除有序数组中的重复项2
1.题目
2.解题思路
由于给定数组是有序的,所以相同元素必然连续。我们可以使用双指针解决本题。遍历数组检查每一个元素是否应该被保留,如果应该被保留,就将其移动到指定位置。具体地,我们定义两个指针慢指针slow和快指针fast。
慢指针slow用来表示处理出的数组长度,快指针fast用来表示已经检查过的数组长度。即nums[fast]表示待检查的第一个元素。nums[slow-1]为上一个被保留的元素所移动到的指定位置。
本题要求相同元素最多出现两次而非一次,所以我们需要检查上上个应该被保留的元素nums[slow-2]是否和当前待检元素nums[fast]相同。
● 当且仅当nums[slow-2] = nums[fast]时,待检查元素nums[fast]不应该被保留(此时:nums[slow-2] = nums[slow-1]=nums[fast]),fast后移一位。
● 当nums[slow-2] != nums[fast]时,将fast指向元素的值复制给slow所指向的元素,然后slow和fast同时向后移一位。
● 特别的,数组前两个数必然可以被保留,因此对于长度不超过2的数组,不需要进行处理。对于长度超过2的数组,我们指直接将双指针的初始值设置为2即可。
● 最后,slow为处理好的数组长度
3.图解
4.参考代码
int removeDuplicates(int* nums, int numsSize) {
int slow = 2,fast = 2;
if(numsSize < 2)
return numsSize;
while(fast < numsSize)
{
if(nums[slow-2] != nums[fast])
{
nums[slow] = nums[fast];
++slow;
}
++fast;
}
return slow;
}