LeetCode 80. 删除排序数组中的重复项 II(中)

原地算法解决重复项,参照90题 while 的用法,配合Python内建API删除重复的数值即可。

题目

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例

示例 1:

1
2
3
4
5
给定 nums = [1,1,1,2,2,3],

函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3

你不需要考虑数组中超出新长度后面的元素。

示例 2:

1
2
3
4
5
给定 nums = [0,0,1,1,1,1,2,3,3],

函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3

你不需要考虑数组中超出新长度后面的元素。

考察知识点

双指针、数组

核心思想

方法一、单指针
循环遍历,发现连续三个一样的就删去第三个。
调用了python内建的 del 函数完成删除。

方法二、双指针计数删除
用一个变量 cnt 来记录还允许有几次重复,cnt 初始化为1,如果出现过一次重复,则 cnt 加1。当cnt大于2时,说明出现第三次了,删除当前项,指针先回退再前进。当cnt小于等于2时,说明当前项出现次数小于2,调用了python内建的 pop 函数。

方法三、双指针覆盖
用 count 记录当前数字出现的次数。count 的最小计数始终为 1。
若当前元素与前一个元素相同,即 nums[i]==nums[i-1],则 count++。若 count > 2,则说明遇到了多余的重复项。在这种情况下,我们只向前移动 i,而 j 不动,i是快指针,j是慢指针
若 count <=2,则我们将 i 所指向的元素移动到 j 位置,并同时增加 i 和 j。
若当前元素与前一个元素不相同,即 nums[i] != nums[i - 1],说明遇到了新元素,则我们更新 count = 1,并且将该元素移动到 j 位置,并同时增加 i 和 j。
注意,这种方法没有真正的将数据从list中删除,而是向前覆盖了。

Python版本

  • 方法一单指针算法的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from typing import List

class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
p = 0
while p < len(nums):
while p + 2 < len(nums) and nums[p] == nums[p + 1] == nums[p + 2]: # 遇到重复的数字了
del nums[p+2] # equals nums.pop(p+2)
p += 1
return len(nums)

Input = [[1, 1, 1, 2, 2, 3], [0, 0, 1, 1, 1, 1, 2, 3, 3]]
Answer = [[1, 1, 2, 2, 3], [0, 0, 1, 1, 2, 3, 3 ]]

if __name__ == "__main__":
solution = Solution()
for i in range(len(Input)):
print("-"*50)
reslut = solution.removeDuplicates(Input[i])
print(reslut == len(Input[i]))

时间复杂度\(O(n) + O(n^2)\),循环遍历了整个数组,每次删除操作 del 的时间复杂度是 \(O(n^2)\),所以总的时间复杂度为 \(O(n) + O(n^2)\)
空间复杂度\(O(1)\),仅仅申请了一个常数级别的额外空间。
执行用时 :40 ms, 在所有 Python3 提交中击败了91.11%的用户
内存消耗 :13.5 MB, 在所有 Python3 提交中击败了5.08%的用户

  • 方法二双指针计数方法的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution(object):
def removeDuplicates(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
i, count = 1, 1
while i < len(nums):
if nums[i] == nums[i - 1]:
count += 1
if count > 2:
nums.pop(i)
i-= 1
else:
count = 1
i += 1

return len(nums)

时间复杂度:让我们看看最耗时的操作是什么: 我们必须遍历数组中的所有元素,若数组中有 N 个元素,则花费的时间为 \(O(N)\)。删除多余的重复项,del 操作也是 \(O(N)\)。最糟糕的情况是数组中的元素都相同,则我们需要执行 N-1 次的删除操作,则需要花费\(O(N^2)\)。 则,总的复杂度:\(O(N) + O(N^2)\)
空间复杂度\(O(1)\),我们在原地修改数组。
执行用时 :44 ms, 在所有 Python3 提交中击败了84.12%的用户 内存消耗 :13.4 MB, 在所有 Python3 提交中击败了5.08%的用户

  • 方法三双指针覆盖的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution(object):
def removeDuplicates(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
j, count = 1, 1
for i in range(1, len(nums)):
if nums[i] == nums[i - 1]:
count += 1
else:
count = 1
if count <= 2:
nums[j] = nums[i]
j += 1

return j

时间复杂度:\(O(N)\),我们遍历每个数组元素一次。
空间复杂度:\(O(1)\)
执行用时 :40 ms, 在所有 Python3 提交中击败了91.11%的用户
内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.08%的用户

参考连接

LeetCode官方题解