[LeetCode刷题笔记]26 - 删除有序数组中的重复项(C++/Python3/Java/双指针)

一、题目描述

  • 给你一个升序排列的数组 nums,请你原地删除重复出现的元素,使每个元素只出现一次,返回删除后数组的新长度。元素的相对顺序应该保持 一致。
  • 由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有k个元素,那么nums的前k个元素应该保存最终结果,将最终结果插入nums的前k个位置后返回k
  • 不要使用额外的空间,你必须在原地修改输入数组,并在使用O(1)额外空间的条件下完成。

示例:

输入 输出 解释
nums = [1,1,2] 2, nums = [1,2,_ ] 函数应该返回新的长度 2,并且原数组nums的前两个元素被修改为1, 2。不需要考虑数组中超出新长度后面的元素。
nums = [0,0,1,1,1,2,2,3,3,4] 5, nums = [0,1,2,3,4] 函数应该返回新的长度5,并且原数组nums的前五个元素被修改为0, 1, 2, 3, 4。不需要考虑数组中超出新长度后面的元素。

提示:

  • 0 < = n u m s . l e n g t h < = 3 ∗ 1 0 4 0 <= nums.length <= 3 * 10^4 0<=nums.length<=3104
  • 1 0 4 < = n u m s [ i ] < = 1 0 4 10^4 <= nums[i] <= 10^4 104<=nums[i]<=104
  • nums 已按升序排列

进阶:

该问题的通用解法?即扩展到数字可重复 k k k 位。


二、求解思路:双指针法

  • 由于数组nums是有序数组,所有相等元素必定是挨在一起的,利用这一特性我们可以采用双指针的方法解题。
  • 定义两个指针 fastslow 分别为快指针和慢指针,快指针表示遍历数组到达的下标位置,慢指针表示下一个不同元素要填入的下标位置,初始时两个指针都指向下标值1。
  • 快指针遍历整个数组,判断的条件为是否与上一元素相同,不同则将其放入慢指针指向的位置,并将慢指针加一,相同则快指针加一。
  • 优化:当nums本身就是无重复元素时,该算法会一直进行原地赋值,为了防止原地赋值,我们可以添加一个小判断,当 fast - slow > 0 时,才进行赋值。

C++代码

int removeDuplicates(vector<int>& nums) {
    int n = nums.size();
    if (n == 0) {
        return 0;
    }
    int fast = 1, slow = 1;
    while (fast < n) {
        if (nums[fast] != nums[fast - 1]) {
            if(fast - slow > 0) {
				nums[slow] = nums[fast];
			}
            ++slow;
        }
        ++fast;
    }
    return slow;
}

Python代码

def removeDuplicates(nums: List[int]) -> int:
    if not nums:
        return 0
    
    n = len(nums)
    fast = slow = 1
    while fast < n:
        if nums[fast] != nums[fast - 1]:
            if(fast - slow > 0):
				nums[slow] = nums[fast]
            slow += 1
        fast += 1
    
    return slow

Java代码

public int removeDuplicates(int[] nums) {
	int n = nums.length;
    if (n == 0) {
        return 0;
    }
    int fast = 1, slow = 1;
    while (fast < n) {
        if (nums[fast] != nums[fast - 1]) {
            if(fast - slow > 0) {
				nums[slow] = nums[fast];
			}
            ++slow;
        }
        ++fast;
    }
    return slow;
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n 是数组的长度。快指针和慢指针最多各移动 n 次。
  • 空间复杂度: O ( 1 ) O(1) O(1),只需要使用常数的额外空间。

三、通解:扩展到数字至多可重复 k 位。

  • 遍历初值为0,而判断条件idx < k,目的是将前k个元素直接复制,此时产生了或短路,因此后面条件无需判断,不会出现 idx - k < 0 的情况。

C++代码

int removeDuplicates(vector<int>& nums) {
    return process(nums,1);
}
int process(vector<int>& nums,int k){
    int idx = 0;
    for(auto x : nums){
        if(idx < k or nums[idx - k] != x){
            nums[idx++] = x;
        }
    }
    return idx;  
}

Python代码

def removeDuplicates(self, nums: List[int]) -> int:
    def process(nums, k):
        idx = 0
        for x in nums:
            if idx < k or nums[idx-k] != x:
                nums[idx] = x
                idx += 1
        return idx
    return process(nums, 1)

Java代码

public int removeDuplicates(int[] nums) {   
    return process(nums, 1);
}
int process(int[] nums, int k) {
    int idx = 0; 
    for (int x : nums) {
        if (idx < k || nums[idx - k] != x) nums[idx++] = x;
    }
    return idx;
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

你可能感兴趣的:(LeetCode刷题笔记,leetcode,算法,数据结构,双指针,1024程序员节)