13. 删除有序数组中的重复项 II
给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例:
输入:nums = [1,1,1,2,2,3] 输出:5, nums = [1,1,2,2,3] 解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 [1, 1, 2, 2, 3] ,不需要考虑数组中超出新长度后面的元素。
常用的算法是:双指针法 、快慢指针法 和 函数法 。
双指针法:(此方法思路是对的,但是它创造了额外的空间)
双指针法的思路:使用两个指针来遍历数组,同时用一个计数器来记录当前元素出现的次数,如果超过两次就跳过,否则就覆盖到前面的位置。这样可以保证前面的元素都是符合条件的,而后面的元素都是多余的。
# 双指针法 时间复杂度:O(n) 空间复杂度:O(1)
def remove_dupulicates1(nums):
# 如果数组为空或之只有一个元素,直接返回数组长度
if len(nums) <= 1:
return len(nums)
# 初始化两个指针 和 一个计数器,i 指向第一个元素, j 指向第二个元素
i, j, count = 0, 1, 1
# 遍历数组,直到 j 到达末尾
while j < len(nums):
# 如果 j 指向的元素 和 i 指向的元素相同,说明有重复, 更新计数器
if nums[j] == nums[i]:
count += 1
# 如果计数器小于2, 说明当前元素还可以出现一次,就将它放到 i 的下一个位置,并更新 i 和 j
if count <= 2:
nums[i + 1] = nums[j]
j += 1
i += 1
# 否则计数器大于2,说明当前元素不可以出现了,更新 j 的位置
else:
j += 1
# 否则,重置计数器 count
else:
count = 1
return i + 1
快慢指针法:(此方法是从双指针法优化而来的)
快慢指针法的思路:使用两个指针来遍历数组,快指针用来检查每个元素是否符合条件,慢指针用来覆盖要保留的元素。如果快指针指向的元素和慢指针及其前一个元素相同,说明这个元素已经出现了两次,需要跳过,否则就覆盖到慢指针的下一个位置。这样可以保证前面的元素都是符合条件的,而后面的元素都是多余的。
# 快慢指针 时间复杂度:O(n) 空间复杂度:O(1)
def remove_dupulicates2(nums):
# 元素可以重复2次,慢针可以从第二个元素开始,快针可以从第三元素开始
slow = 1
# 遍历数组,直到fast到达末尾
for fast in range(2, len(nums)):
# 判断快针元素是否与慢针和慢针前一个元素是否相等
if nums[fast] == nums[slow] and nums[fast] == nums[slow-1]:
continue
slow += 1
nums[slow] = nums[fast]
return slow + 1
函数法:(此方法思路是对的,但是它创造了额外的空间,但在力扣却能运行,我也不懂为什么能正确运行)
函数法的思路:使用Counter类来统计每个元素出现的次数,然后将超过两次的元素减少到两次,最后将Counter对象转换为列表,并覆盖原数组。这个方法的优点是简洁易懂,缺点是需要额外的空间来存储Counter对象。
# 函数法 时间复杂度:O(n) 空间复杂度:O(n)
def remove_dupulicates3(nums):
dict = Counter(nums)
for i in dict:
if dict[i] > 2:
dict[i] = 2
list1 = list(dict.elements())
nums.clear()
nums.extend(list1)
return len(nums)
题目来源:力扣