1. 题目地址
2. 题目描述
- 英文描述
Given a sorted array nums, remove the duplicates in-place such that each element appears only once and returns the new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
Clarification:
Confused why the returned value is an integer but your answer is an array?
Note that the input array is passed in by reference, which means a modification to the input array will be known to the caller as well.
Internally you can think of this:
// nums is passed in by reference. (i.e., without making a copy)
int len = removeDuplicates(nums);
// any modification to nums in your function would be known by the caller.
// using the length returned by your function, it prints the first len elements.
for (int i = 0; i < len; i++) {
print(nums[i]);
}
Example 1:
Input: nums = [1,1,2]
Output: 2, nums = [1,2]
Explanation: Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn't matter what you leave beyond the returned length.
Example 2:
Input: nums = [0,0,1,1,1,2,2,3,3,4]
Output: 5, nums = [0,1,2,3,4]
Explanation: Your function should return length = 5, with the first five elements of nums being modified to 0, 1, 2, 3, and 4 respectively. It doesn't matter what values are set beyond the returned length.
Constraints:
0 <= nums.length <= 3 * 104
-104 <= nums[i] <= 104
nums is sorted in ascending order.
- 中文描述
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组,并在使用 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。
你不需要考虑数组中超出新长度后面的元素。
说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
3. 解题思路
- 注意题目的关键点有4个:
(1)排序数组:输入数组是个有序数组,相同的重复元素是出现在一起的。
(2)原地删除重复项:说明是需要将后面的元素覆盖在需要删除的重复元素上,不申请新数组。
(3)不使用额外空间:肯定不能使用先统计每个元素出现的个数,再重新生成新数组的方法了。
(4)最后需要返回删除重复项后的新数组的长度。 - 解题重点:双指针
(1)采用快慢双指针:使用快慢指针来记录遍历的下标。
(2)开始时这两个指针都指向第1个数字。
(3)如果快慢两个指针所指的数字相同,则快指针向前走一步。
(4)如果快慢两个指针所指的数字不同,则两个指针都向前走一步(慢指针往前走一步,快指针所指的值赋给慢指针,同时快指针也往前走一步),这一步是实现删除重复元素的操作。
(5)当快指针走完整个数组后,慢指针所在位置其实就是最终去重后数组的末尾元素的位置。因此,慢指针当前的坐标加 1 就是数组中不同数字的个数。
(6)时间复杂度:O(n)
(7)空间复杂度:O(1)
解题步骤解析:
最开始的时候两个指针都指向第1个数字,所以快指针就开始向前走一步了。
这个时候慢指针指向第1个数字,快指针指向第2个数字,如果这两个数字相同,那么快指针继续往前走,快指针指向的就是第3个数字,慢指针还指在第1个数字上。
此时再次比较快慢指针所指的元素是否相同:
如果相同,说明前3个数字都是相同的,快指针就继续往前走。
如果不同,那么说明第1个数字和第2个数字是相同的,第3个数字是不同的,所以此时:(1)慢指针往前走一步,(2)快指针所指的值赋给慢指针,即将第3个位置上的元素挪到第2个位置,去除了重复项。(3)快指针往前走一步,继续遍历下一个元素。
再次比较快慢指针所指元素是否相同,重复之间的步骤。
我们用更极端的一种情况来分析一下所指元素不同时的那一步操作:
如果每个数字都不同,那么最开始时两个指针都指向第1个数字,快指针开始向前走一步。此时快慢指针所指的元素不相同:(1)慢指针往前走一步,(2)快指针和慢指针此时又指向了相同的位置,快指针所指的值赋给慢指针,其实是同一个值的赋值操作,快指针往前走一步。
再次比较快慢指针所指元素是否相同,重复之前的步骤。
当快指针走完整个数组后,慢指针所在位置其实就是最终去重后数组的末尾元素的位置。所以此时慢指针当前的坐标加 1 就是数组中不同数字的个数。
4. 解题关键
- 要想到快慢双指针的操作
- 第1步:快慢两个指针同时指向第1个元素。
- 第2步:若快慢两个指针所指的数字相同,则快指针向前走一步。
- 第3步:若快慢两个指针所指的数字不同,则两个指针都向前走一步(慢指针往前走一步,快指针所指的值赋给慢指针所指位置,同时快指针也往前走一步)。
- 第4步:当快指针走完整个数组后,慢指针当前的坐标加 1 就是数组中不同数字的个数。
- 要点
- 输入是排序数组
- 原地删除重复项
- 不使用额外空间
- 注意nums为空时的边界条件
- 复杂度
- 时间复杂度:O(n)
- 空间复杂度:O(1)
5. 示例代码
python
# coding:utf-8
'''
# @Method:快慢指针
# @Author: wlhr62
'''
import os
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
if nums:
slow = 0
fast = 0
while(fast != len(nums)):
if nums[fast] == nums[slow]:
fast += 1
else:
slow += 1
nums[slow] = nums[fast]
fast += 1
return slow+1
else:
return 0
if __name__ == "__main__":
nums = [1, 1, 2]
A = Solution()
res = A.removeDuplicates(nums)
print(res)
在leetcode上AC的结果: