力扣26. 删除排序数组中的重复项 详解快慢指针各种应用情况 C语言实现

题目如下:

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

实例1:
给定数组 nums = [1,1,2], 

函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 

你不需要考虑数组中超出新长度后面的元素。
实例2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],

函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。

你不需要考虑数组中超出新长度后面的元素。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array

解析如下:

什么是快慢指针?

定义两根指针,移动速度一快一慢,以此来达到自己想要的两根指针间的差值,以找到自己想要的值。

快慢指针几个常见的应用场景?
一:找中间值:

(通常可以应用于链表)
定义两个指针,一快一慢,快指针一次移动两个位置,慢指针一次移动一个位置,那么,当快指针移动到链表尾的时候,慢指针的位置就恰好是链表的中间值。过程如下图示:
在这里插入图片描述

二:判断链表中的环:

定义两个指针,一快一慢,那么在一个环形的链表中,就像在5000米比赛的操场跑道中,跑的慢的人会被跑的快的人超圈而被淘汰,那么一定会有一刻相遇而被超圈。
那么环形链表中同样,如果是环形的,一个指针快,一个指针慢,慢指针的如果有一刻和快指针处于同一位置,就一定有环存在,而如果快指针在等于慢指针之前已经到达了链表尾即(null) 那么就一定没有环存在。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

若存在环,如何找到环的入口点?

由于fast移动步长为2,low移动步长为1,则在low进入环后继续绕环遍历一周之前fast必然能与low重合(且必然是第一次重合)
在这里插入图片描述
如图中所示,设链起点到环入口点间的距离为x,环入口点到问题1中fast与low重合点的距离为y,又设在fast与low重合时fast已绕环n周(n>0),且此时low移动总长度为s,则fast移动总长度为2s,环的长度为r。则
s + nr = 2s,n>0
s = x + y
得 s = nr
代入得
nr = x + y
x = nr - y
现让一指针p1从链表起点处开始遍历,指针p2从第一次相遇处开始遍历,且p1和p2移动步长均为1。则当p1移动x步即到达环的入口点,由③式可知,此时p2也已移动x步即nr - y步。由于p2是从第一次相遇处处开始移动,故p2移动nr步是移回到了第一次相遇处,再退y步则是到了环的入口点。也即,当p1移动x步第一次到达环的入口点时,p2也恰好到达了该入口点。

三:输出倒数第n个节点

定义两个指针,第一个指针首先向前走n-1步,第二个指针保持不动;从第n步开始,第二个指针也开始从链表的头指针开始遍历。由于两个指针的距离保持在n-1,当第一个指针到达链表的尾节点时候,第二个指针正好是倒数第n个节点。
例如,删除倒数第三个节点:
在这里插入图片描述

四:输出倒数第n个节点

第一部分——判断单个链表是否有环
使用两个指针,一个快指针,一个慢指针,快指针一次走两步,慢指针一次走一步;
若快指针最后变为空,则单链表为无环单链表,返回空指针;
若快慢指针在某一时刻指向同一节点,即二者完全一样,则为有环单链表,
此时让快指针重新指向链表头结点,然后快慢指针均一次走一步,
当快慢指针再次相同时,则此节点即为链表入环节点,将其返回。
第二部分——判断链表是否相交
情况一:两链表中一个为有环链表,一个为无环链表,则此时两链表一定不相交,返回空即可;(如果两个链表相交,一个链表有环的话,另一个链表也一定有环,否则一定不相交)
在这里插入图片描述
情况二:两个链表都是无环链表,此时有两种情况,1)两个无环链表相交;2)两个无环链表不相交;
在这里插入图片描述
首先遍历两链表,分别得到两个链表的长度,
取两个长度的差值,然后让长链表先遍历差值长度,此时长链表剩余部分与短链表长度相同,
然后两条链表同时遍历,直到遇到相同节点为止,若相同节点为空,则两链表不相交,返回空,
否则两链表相交,返回相交节点即可;
情况三:两个链表都是有环链表,此时有三种情况:

  1. 两个有环链表不相交;
  2. 两个有环链表相交,且交点在环外;
  3. 两个有环链表相交,且交点在环内。

首先获得两个链表的入环节点,若入环节点相同,则可转变为情况二,此时将入环节点作为链表的终止节点即可;
在这里插入图片描述
若两个入环节点不同,以一个入环节点开始遍历,若遍历一遍过程中都没有遇见另一个入环节点,则两链表不相交,返回空,若遇到另一入环节点,则说明两链表相交,此时返回任意一个入环节点即可。
在这里插入图片描述

参考链接:https://www.nowcoder.com/questionTerminal/db55f7f21127403cb268ffad9d23af37?orderByHotValue=1&mutiTagIds=139_580_151&page=1&onlyReference=false
来源:牛客网

接下来回到本题

本题要求将一个已经排好序的数组去重,可以使用快慢指针的方法,快指针一次移动一个位置,慢指针只有满足快慢指针所指的值不同时才向前移动。
这样,在快指针没有移动到尾部时:
如果快指针与慢指针的值不相等,将快指针的值赋值给慢指针前面一位,慢指针向前走一位。
在这里插入图片描述

代码如下:
int removeDuplicates(int* nums, int numsSize){
    int fast = 1;
    int slow = 0;
    int length = 0;
    if(numsSize == 0){
        return 0;
    }
    while(fast <= numsSize -1){
        if(nums[fast] != nums[slow]){
            int temp;
            temp = nums[fast];
            nums[slow + 1] = nums[fast];
            slow++;
            length++;
        }
        fast++;
    }
    return length + 1;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值