前言
这篇继续磕算法。这道题同样来自leetcode,链接在这里leetcode.com/problems/re… 。题目要求可以保留两个及以下的重复数字,是去重到只留下唯一值题目的进阶版。这个题目同时还要求我们的space complexity只能是O(1),我们无法再新建一个数组来解决题目,只能在原数组内部交换数字的位置。
暴力破解
去重一个数组可以通过set,这个方法可以去重至只留下唯一值。只留下唯一值也可以用暴力循环:找到和上一个数不一致的下一个数,然后把它替换到上一个数的后面。但是这个题目我尝试了几次暴力破解都失败了。总会在一些特别的test case上报错。下面讲的这个算法就可以无痛解决所有去重问题,不论题目要求可以容许多少个重复值。
算法
首先我们要先明确题目中的条件:这个数组是已经排好序的,后面的数字是一定大于或者等于前面的数字的。我们用 i来标记重复的位置,用 j 来遍历数组。i首先会被设为0。假设题目允许的重复数量为k,那么在这个数组前k个数都是符合要求的。从k+1个数开始,我们就检查它是否大于它前k-2个数,如果大于就代表这个数字并不重复。如果等于的话就代表它重复了。这个时候我们把这个数和 i 标记位置的数进行交换。
例子
假设一个数组是这样的,且k=2,我们可以容许2两个重复的数值:
nums = [0,0,1,1,1,1,2,3,3]
初始化 i = 0 , j = 0
在 j < 2的时候i和j一起移动。
直到 j = 2,此时我们就判断条件 nums[j] > nums[j - 2] 是否成立,如果成立,就继续移动j,增加i。
这个数组直到 j = 4时就不成立了,因为此时1的数量已经超过了两个,这时我们只移动j不移动i,标记重复值的位置。
然后我们继续移动j,直到 j = 6 时nums[j] > nums[j - 2]重新成立,这时我们交换nums[i]和nums[j]的位置,并移动i至下一个位置。
最后我们返回i,这个数组前i个数都是符合条件的数字。
代码
这个题目的代码也非常简单:
func removeDuplicates(nums []int, k int) int {
i := 0
for j := 0; j < len(nums); j++ {
if j < k || nums[j] > nums[i - k] {
nums[i] = nums[j] i++
}
}
return i
}