2023-7-14Leecode练习

题目:

给你一个升序排列的数组 nums ,请你原地删除重复出现的元素,使每个元素只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
返回 k 。
[题目链接] (https://leetcode.cn/problems/remove-duplicates-from-sorted-array/description/)

判题标准:

系统会用下面的代码来测试你的题解:
int[] nums = […]; // 输入数组
int[] expectedNums = […]; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用
assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}
如果所有断言都通过,那么您的题解将被通过。
【解析】“assert”即Python断言机制,简单的说就是如果assert后紧跟的语句是“False” ,此时程序直接停止运行。详细参考下面这篇博文:
参考链接: python assert的用法介绍(附代码)

示例:

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

官方解法:双指针法

方法简介:

1.这道题目的要求是:对给定的有序数组 nums 删除重复元素,在删除重复元素之后,每个元素只出现一次,并返回新的长度,上述操作必须通过原地修改数组的方法,使用 O(1)的空间复杂度完成。
【解析】这段话需要注意的点有3个:
① “有序”数组,并且根据题目可知数组已经按照升序给排好了。
② 返回值:返回新的长度。
③ 原地修改数组的方法。

2.由于给定的数组 nums是有序的,因此对于任意 i<j,如果 nums[i]=nums[j],则对任意 i≤k≤j,必有 nums[i]=nums[k]=nums[j],即相等的元素在数组中的下标一定是连续的。利用数组有序的特点,可以通过双指针的方法删除重复元素。

【解析】这段话的大前提是“有序”,i和j对应的是数组的索引。
① 对于任意的 i < j,nums[i] = nums[j]。Eg:nums = [1,1,2],假设i 等于0,j 等于1,此时 nums[i] 即 nums[0] 就等于1,nums[j] = nums[1] 也等于1,符合这个小前提了。
② 则对任意 i≤k≤j,必有 nums[i]=nums[k]=nums[j]。此时使用①中的Eg去理解就不是非常直观了,所以我们再给出一个Eg:nums = [1,1,1,2] ,假设i = 0,k = 1 ,j = 2 ,此时符合小前提 i<j ,nums[i] = nums[j] = 1。也符合 i≤k≤j ,nums[i]=nums[k]=nums[j]。所以我认为“则对任意 i≤k≤j,必有 nums[i]=nums[k]=nums[j]”这句话的意思就是在说明数组中有至少3个一样的数字,且由于原数组已经按照升序排序,所以这3个数字是连续的。故得出结论:“即相等的元素在数组中的下标一定是连续的”。

3.如果数组 nums的长度为 0,则数组不包含任何元素,因此返回 0。
【解析】这里提示了一种特殊情况,在算法设计过程中最容易忽略的就是这种特殊情况和边界问题了,所以官方也给了提示。

4.当数组 nums 的长度大于 0 时,数组中至少包含一个元素,在删除重复元素之后也至少剩下一个元素,因此 nums[0]保持原状即可,从下标 1 开始删除重复元素。
【解析】当nums长度大于0时,也就是普通的情况了,即给出一个任意数组。有以下2种情况:
① 数组中没有重复元素:Eg:[1] ,[1,2]
对于nums = [1] , 则更新后仍然为nums = [1]
② 数组中有重复元素:Eg:[1,1]
对于nums = [1, 1] ,则更新后为nums = [1]
综上我们可以发现,对于长度大于0的数组,无论是否有重复元素,更新前和更新后的nums[0] 都保持不变。故得出结论:“因此 nums[0]保持原状即可,从下标 1 开始删除重复元素”。

5.定义两个指针 fast和 slow分别为快指针和慢指针,快指针表示遍历数组到达的下标位置,慢指针表示下一个不同元素要填入的下标位置,初始时两个指针都指向下标 1。
【解析】
① 快指针就理解为普通遍历数组的指针就可以了,就是我们平常一个一个遍历数组中元素那种指针。
② “慢指针表示下一个不同元素要填入的下标位置”,这句话拆开来理解,“慢指针表示-下一个不同元素-要填入的下标位置”。

6.假设数组 nums长度为 n。将快指针 fast 依次遍历从 1 到 n−1 的每个位置,对于每个位置,如果 nums[fast] ≠ nums[fast−1]
,说明 nums[fast] 和之前的元素都不同,因此将 nums[fast] 的值复制到 nums[slow],然后将 slow 的值加 1,即指向下一个位置。
【解析】
① fast指针从1开始原因参考4。
② “对于每个位置,如果 nums[fast] ≠ nums[fast−1]
,说明 nums[fast] 和之前的元素都不同”,注意这段话的大前提,也就是这道题目的大前提:数组是按照升序已经排列好的。所以可以得出此结论,如果数组中任意连续两项元素不相等,那么这任意连续两项元素中的后项元素和位于它之前的所有元素一定是不相等的。
Eg1:nums = [1,2,3,4,5] ,fast = 2 ,则 fast - 1 = 1 ,nums[fast] =3 , nums[fast - 1] = 2 ,此时nums[fast]也就是3和之前的1,2都是不相等的。
Eg2:nums = [1,2,2,3,4,5] ,fast = 2,则fast - 1 = 1 ,nums[fast] = 2 ,nums[fast - 1] = 2 , 此时 nums[fast] 也就是2和位于它之前的元素[1 ,2]有了“2”这个相等元素。
综上就可以得出“如果 nums[fast] ≠ nums[fast−1]
,说明 nums[fast] 和之前的元素都不同"。
③ 基于①和②,就很好理解slow指针了,slow指针指的是”下一个不同元素的指针。因为nums[fast]已经是非重复元素了,也就是说此时我们已经把非重复元素给找出来了,下一步就是移动了,所以得出“因此将 nums[fast] 的值复制到 nums[slow]”。
④ 在③的基础上,也就是已经将非重复元素移动完成后,slow的值加1,为下一次移动非重复元素做好准备。此时再回头看5中的②,应该就完全理解啦。

7.遍历结束之后,从 nums[0] 到 nums[slow−1]的每个元素都不相同且包含原数组中的每个不同的元素,因此新的长度即为 slow,返回 slow即可。
【解析】这里需要注意的点就是数组长度和数组的下标之间差值为1,数组下标从0开始。

附上官方提供的Python3源码:

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

        return slow

代码解析:

1.第一个if not nums ,上来先排除特殊情况,如果nums数组为空那么直接返回0,这也是算法设计中最容易忽略的。需要注意的是如果 nums = [] ,那么它的布尔值是 False , Python中还有许多类似的,例如空元组等等,其布尔值均为False。详细请参考这篇博文:
参考博文: python None、 空字符串 ‘‘、 0、 空对象(空字典 {})、 空数组[] 的布尔值(boolean)都为false
2.我认为本题最重要的就是slow指针移动的条件,即代码的 if 语句开始的3行。
3.非常建议大家手动走一遍fast指针和slow指针,就会非常清晰明了啦!
4.Leecode官方题解也给了指针移动的案例:就在Leecode题解页面。附上题解页面链接:
本题题解链接

官方案例

复杂度分析:

时间复杂度:O(n),其中 n 是数组的长度。快指针和慢指针最多各移动 n 次。
【解析】需要注意的是时间复杂度计算是按照“最坏的情况”下代码需要执行的次数。
空间复杂度:O(1)。只需要使用常数的额外空间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值