排序算法系列题(双指针应用)

一.删除有序数组中的重复项(简单)

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;
}
  • 41
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值