一个简单的问题我糊涂了很久才搞好,这是我的一些解题思路,希望大神们指点,指导我更好的解答。


这是一个数组原地去重的问题,最多保留k个重复元素,然后返回原地去重后新数组的大小。

我的思路是这么发展的:

原文链接如下:http://t.csdn.cn/8tU72


LeetCode26:给你一个有序数组nums,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。不要使用额外的数组空间,你必须原地修改数组,在空间复杂度O1的情况下完成。


这个题的思路在于需要保存重复元素的值。如果我能保存重复元素的值,那么负责遍历数组的元素就能做出判断,如果是重复元素值就继续前进,直到不是该值为止。在加上算法通关村这章是双指针,所以这里我使用了双指针:


slow代表去重后的有效元素,[0,slow]是去重的结果区间;fast用于遍历数组。


如下图所示,刚开始slow==fast==0,然后fast不断前进直到不是该重复值(此处为1)为止。然后slow++,并将fast遍历到的新值赋给arr[slow],这样arr[fast]根据是否==arr[slow]继续前进,直到不是arr[slow]的值为止。此时arr[fast]就是一个新值,再赋给arr[++slow]。这样每有一个新的不同值就存储在[0,slow]区间中,从而实现了去重。


这类问题要处理边界情况,用for循环是更好的。


public static int deleteDuplicate(int[] arr){

int slow = 0;

for (int fast = 0; fast < arr.length; fast++) {

if (arr[fast] != arr[slow]){

slow++;

arr[slow] = arr[fast];

}

//每次循环fast都前进一步,如果不等于arr[slow](找到新值)了,就赋值给arr[++slow]

//如果等于arr[slow](是重复值)了,那么slow不做任何改变,fast继续++判断下一个元素

}

return slow + 1;

}

可以看到,每次循环fast只走一步,最后退出循环时刚好是arr.length-1索引,就不用纠结越界问题了,因为该for循环中不会出现越界。



但如果每个元素可以出现一次或两次,也就是最多可以出现两次,又该如何应对呢? (LeetCode80)例:输入【1,1,1,2,2,3】 输出【1,1,2,2,3】


我认为这类问题的核心实际上是:fast指针在遍历数组时,判断哪些值可以放进[0,slow]结果区间。


在前面的去重问题中,只有当arr[fast] != arr[slow]时,arr[fast]才能放入arr[++slow]中,这样才能确保重复元素只保留一个。


那么最多保留2个的时候,哪些值可以放进结果区间呢?


答案是:1.新值,即arr[fast] != arr[slow]的可以放入[0,slow]的区间。


2.重复值的第二个元素,即arr[fast] == arr[slow],但是arr[fast]是第二个重复值时,这种情况也可以放入[0,slow]区间。如下图,fast遍历至第二个2时arr[slow]已经==2,但这个2是可以加入[0,slow]区间的。



以此类推,如果最多保留K个元素,那么这些值可以放进结果区间:是新值的,和是第2~第K个重复值的。第K+1个重复值及以上不能放入结果区间。


那么现在我们注重编码的点,就落在了如何确定当前元素是第二到第K个重复元素上。


思路一:arr[fast-k] != arr[fast] 有何不妥?


不瞒大家,我最开始想到的其实是这个。也许你也这么想,如果限制只能是第二到第K个元素,那么因为第二到第K个重复值的元素,往前K个就是原数组中不同值的元素了,两者值不等;而第K+1个元素,往前K个就是第一个重复元素,值相等。所以限制条件应该是:


arr[slow] != arr[fast] || fast < k || (arr[slow] == arr[fast] && arr[fast-k] != arr[fast] )


乍一看,这不是很对吗?


但是问题在这:经过对arr[slow]的赋值,[0,slow]区间的元素已经发生改变,不再是有序的。此时如果还认为第K个重复值元素往前K个的值是不同的,可能会出问题。


如下图,是最多保留2个重复元素(k==2)的情况,里面就出现了问题。



如图,k==2,所以第三个2被舍弃了,同时fast移动到第一个3,是新值,所以把原来第三个2的位置(slow所指的位置)赋值成了3。你发现了没有,3的个数改变了。此时原本的第2个3,也就是第二张图中fast指向的元素,居然变成《第3个》3了。再进行arr[fast-k] != arr[fast] 时,就离谱地判false了,而它本应该加入[0,slow]结果区间的。


这就是原地修改的弊病:给arr[slow]赋值时修改了原数组前面的值,使前面的元素偏离了初始值。


所以我自己总结出一个结论:在进行双指针的原地修改时,fast指针不能回头看。


也就是不能用arr[fast-k]的意思,不知“回头”这两个字你是否理解透彻了呢?


思路二:正确做法是什么?


如果fast不能回头的话,从一开始我们就不应该往后看,而是应该设定一个变量,记录当前元素是重复值的第几个。代码如下:


public static int deleteDuplicateButSaveMostK(int[] arr,int k){

int slow = 0;

int repeatCount = 1;

//repeatCount是当前值的重复次数,我们从第二个元素开始遍历,此时第一个元素重复次数为1次。

//第一个元素可看做新值,看做已放入[0,slow]结果区间中,此时slow == 0指向该新值

for (int fast = 1; fast < arr.length; fast++) {//fast==1,从第二元素开始遍历

if (arr[fast] != arr[slow]){//arr[fast]是新值就将当前值重复次数置为1次,然后赋值

repeatCount = 1;

arr[++slow] = arr[fast];

}else {

//是重复值就把重复次数++,如果重复次数<=k就赋给arr[++slow]

repeatCount++;

if (repeatCount <= k){

arr[++slow] = arr[fast];

}

//如果重复次数>=k+1就不做处理,fast继续++直到找到新值为止。

}

}

return slow+1;

}


最多保留重复元素个数,从1到K,全部可以用这个方法处理。


菜鸟初出茅庐,希望大神们指点更简单的做法。


0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
krafft
作者分享
一文带你彻底理清 Redisson RateLimiter tryAcquire 分布式限流的底层原理!有图有真相,挑战全网最详细
6
令牌桶算法好有意思😋 附鱼皮在 BI 项目里推荐的限流算法教程:https://juejin.cn/post/6967742960540581918 真的写的很好,尤其伪代码能让人学到不少,部分实现个人觉得有待商榷,比如有几个 long 类型变量应该换 double, 但总体的思路真的 nice, 感觉可以时常温习
5
#在校生刷八股的体会 如果你直接拿着一本八股就开始背,往往就半途而废了,因为觉得很枯燥,并且太多的题目也让你找不到重点,背完这题,其实还没完全掌握,又急着去看下一题,至少我的感受是这样背很影响学习热情。 最近找到的一个方法是,每天到牛客上刷刷面经,看面经里面有哪些你不会的题,然后就到面试鸭里面去搜,搜完还是不太懂就去问AI,让它逐步给你解释。这样就相当于每天去找一个有意思的问题,也不会有那种八股题讲的不太透彻的毛病,似懂非懂的感觉还是让人很难受的,不如我G哥(gpt) 算法也是同理,如果你单纯去刷,可能会有点枯燥,但如果想着总结一点经验发到CSDN,虽然可能会多消耗你的时间,但长线来看能让你保持热情。 虽然我也是个经常性报废的人,但还是和大家共勉吧,不要焦虑,要去做事,不专注于结果,而专注于提升。
7
api开放平台开发客户端sdk要写一个starter,本来是不难的事,结果最后打jar包的时候一直报错,说版本是Java5,这个问题以前也见过很多次,一般改了idea里的setting或者project structure就好了,结果这次,不知道是不是删了pom.xml中的plugin的原因,一直好不了,搞了两个小时差点心态崩了,idea里能改的都改了一遍,百度上搜搜搜,最后都不行 最后突然一下子冷静下来,思考:为什么明明改了idea的配置,结果还是会经常出现Java5的问题?这个问题的根源在哪?搜了一下这个想法,百度给出的回答就和之前完全不同了,最后发现是Maven配置文件的问题,照着文章改了maven的settings.xml,成功解决,文章地址: https://blog.csdn.net/Montaro2017/article/details/107375120 经验教训:遇到这种搞了很久的bug,在百度的同时一定不能太着急,比如你mvn install报错了,不要第一时间百度,还是得冷静下来,一是仔细看报错信息,二是思考,为什么总是会出现某个问题,问题的根源在哪,而不是慌忙地粘贴报错信息到百度上搜索,否则很可能竹篮打水。 #maven #经验 #Java
4
9.11 还是继续开始打卡🤪 今天刷了一道力扣,面试鸭看了两道java基础,api项目几乎没有推动,晚上状态有点差😇😇 明天计划还是一道力扣,项目把starter做出来,面试鸭看个三道以上😅
2
下载 APP