LeetCode题练习与总结:删除有序数组中的重复项

124 篇文章 0 订阅
28 篇文章 0 订阅

一、题目描述

给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k 。

判题标准:

系统会用下面的代码来测试你的题解:

int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案

int k = removeDuplicates(nums); // 调用

assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有断言都通过,那么您的题解将被 通过

示例 1:

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

提示:

  • 1 <= nums.length <= 3 * 10^4
  • -10^4 <= nums[i] <= 10^4
  • nums 已按 非严格递增 排列

二、解题思路

1. 初始化两个指针:i 用于遍历数组 numsj 用于标记当前唯一的元素位置。

2. 遍历数组 nums,对于每个元素:

  • 如果 nums[i] 不等于 nums[j-1](即当前元素与已记录的最后一个唯一元素不同),则将 nums[i] 赋值给 nums[j],并将 j 加一,表示找到了一个新的唯一元素。
  • 如果 nums[i] 等于 nums[j-1],则跳过当前元素,不做任何操作。

3. 遍历结束后,j 的值即为数组中唯一元素的数量,也是新数组的长度。

三、具体代码

class Solution {
    public int removeDuplicates(int[] nums) {
        if (nums == null || nums.length == 0) {
            return 0;
        }
        
        int j = 0; // 标记当前唯一元素的位置
        for (int i = 0; i < nums.length; i++) {
            if (i == 0 || nums[i] != nums[j - 1]) {
                nums[j] = nums[i]; // 遇到不同的元素,记录为唯一元素
                j++; // 更新唯一元素的计数
            }
        }
        return j; // 返回唯一元素的数量
    }
}

四、时间复杂度和空间复杂度

这段代码的时间复杂度是 O(n),空间复杂度是 O(1)。

1. 时间复杂度
  • 算法的核心是一个单层循环,它遍历了整个数组 nums
  • 在每次迭代中,我们执行常数时间的操作,包括比较和可能的赋值。
  • 因此,整个算法的时间复杂度是 O(n),其中 n 是数组 nums 的长度。
2. 空间复杂度
  • 我们没有使用额外的空间来存储数据,所有的操作都是在原数组上进行的。
  • 我们使用了一个额外的变量 j 来跟踪唯一元素的位置,但这个变量的空间需求是常数的,不依赖于输入数组的大小。
  • 因此,算法的空间复杂度是 O(1),即常数空间复杂度。

五、总结知识点

  1. 数组处理:代码处理一个整数数组 nums,这是数据结构中的一个基本概念。

  2. 边界检查:在处理数组之前,代码首先检查数组是否为 null 或者长度为 0,这是良好的编程习惯,可以避免空指针异常或其他潜在的错误。

  3. 原地修改:代码在原数组上进行修改,而不是创建一个新的数组来存储唯一元素。这种方法节省了空间,但要求我们对数组的某些部分进行重新排列。

  4. 双指针技术:使用两个指针 iji 用于遍历数组,而 j 用于标记当前唯一元素的位置。这是一种常见的优化技术,用于在遍历过程中跟踪特定状态。

  5. 条件赋值:在循环内部,使用条件语句 if (i == 0 || nums[i] != nums[j - 1]) 来决定是否将当前元素赋值给 nums[j]。这确保了只有当当前元素与前一个唯一元素不同时,它才会被记录。

  6. 循环控制:循环 for (int i = 0; i < nums.length; i++) 控制了遍历数组的过程,这是循环结构的基本应用。

  7. 数组索引:在赋值操作 nums[j] = nums[i]; 中,使用了数组索引来访问和修改数组元素。

  8. 递增计数器:变量 j 作为计数器,用于记录新数组的长度,这是计数器在循环中的典型应用。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

  • 47
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直学习永不止步

谢谢您的鼓励,我会再接再厉的!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值