LeetCode之算法面试之数组1之移动零(283)、移除元素(27)、删除排序数组中的重复项(26)、删除排序数组中的重复项II(80)

————技巧点:双指针法

1、移动零(283)

题目描述:

【简单题】

在这里插入图片描述
题目链接

思路分析

要求:移动零到数组的末尾

约束:在原数组上操作,不能拷贝额外的数组

题解一:双指针法

  • 首先,遍历整个数组,在遍历的过程中,如果碰到非0的元素,那么就将该元素 向前移动。此时,可设置一个临时指针k(初始为0),碰到非零元素,则将令nums[k]=nums[i],同时k++

  • 当遍历完成后,将最后一个非零元素(nums中k的位置) 到 数组结尾 ,这一部分的位置,全部置为0。

在这里插入图片描述

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        k=0
        for i in range(len(nums)):
            if nums[i]:#如果nums[i]不为零
                nums[k]=nums[i]
                k+=1
        # 非0元素统计完了,剩下的都是0了
		# 所以第二次遍历把末尾的元素都赋为0即可
        for j in range(k,len(nums)):
            nums[j]=0
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

题解二:双指针法

\quad \quad 对题解一进行一个优化,将非零元素与零元素进行交换。

  • 设置一个临时指针即慢指针k(初始为0)。

  • 遍历数组,其中for循环中的i就相当于一个当前指针。

    • 遇到非零元素nums[i],就交换当前指针和慢速指针指向的元素即将nums[i]nums[k]交换,然前进两个指针即k++,i随着循环就前进了。【考虑一个特殊情况:如果首个元素非零时,此时i=k,没有交换的必要,因此只需要将k++。故更优化的情况下,在这个条件下再加一个约束】
    • 如果它是零元素,我们只前进当前指针,k不变

在这里插入图片描述

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        k=0
        for i in range(len(nums)):
            if nums[i]:
                if i!=k:#如果i=k,就没有交换的必要了,所以加了这个约束
                    nums[k],nums[i]=nums[i],nums[k]
                    k+=1
                else:
                    k+=1
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

2、移除元素(27)

题目描述:

【简单题】
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

在这里插入图片描述
说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

在这里插入图片描述
题目链接

思路分析

双指针法

  • 设置一个临时指针即慢指针k(初始为0)

  • 遍历数组,其中for循环中的i就相当于一个当前指针或快指针。

    • nums[i]与给定的值相等时,递增i(因为在循环中,i本身就在递增,所以不用做任何事情) 以跳过该元素。
    • 只要 nums[i] != val,就复制 nums[i]到 nums[k] 并同时递增两个指针(k++,i不用做什么)。重复这一过程,直到i到达数组的末尾,该数组的新长度为k。
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        k=0
        for i in range(len(nums)):
            if nums[i]!=val:
                nums[k]=nums[i]
                k+=1
        return k
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

3、删除排序数组中的重复项(26)

题目描述:

【简单题】

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
在这里插入图片描述
题目链接
思路分析

要求:保留排序数组中每个元素只出现一次

约束:在原数组上操作,不能拷贝额外的数组

\quad \quad 考虑到数组是排序的,依次遍历数组并跳过重复项的话,当前元素只能跟前面的有重复的可能性。此时,我们就可考虑用双指针法。

双指针法

  • 设置一个临时指针即慢指针j(初始为0),辅助判断是否为重复元素。

  • 遍历数组(从第一个元素开始),其中for循环中的i就相当于一个当前指针或快指针。

    • nums[i]=nums[j]时,递增i(因为在循环中,i本身就在递增,所以不用做任何事情) 以跳过该元素。
    • 只要 nums[i] !=nums[j],就复制 nums[i]nums[j] 并同时递增两个指针(j++,i不用做什么)。重复这一过程,直到i到达数组的末尾,该数组的新长度为j+1。

【python代码实现】

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        j=0
        for i in range(1,len(nums)):
            if nums[i]!=nums[j]:
                j+=1
                nums[j]=nums[i]
        return j+1 #因为遍历数组且第一位置开始的,默认nums[0]是存在的,故返回j+1
  • 时间复杂度: O ( n ) O(n) O(n),假设数组的长度是 n,那么i 和 j 分别最多遍历 n 步。

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

4、删除排序数组中的重复项II (80)

题目描述:

【中等题】

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
在这里插入图片描述
题目链接

思路分析

要求:保留排序数组中每个元素最多出现两次,返回移除后数组的新长度。

约束:在原数组上操作,不能拷贝额外的数组

\quad \quad 与leetcode26题唯一不同的是一个元素重复的情况下可以出现两次,所以我们可以在26题的基础上再添加一个count计数指针,统计每个元素出现的次数。

三指针法

  • 设置三个指针,i 是遍历指针,指向当前遍历的元素;j 指向下一个要复制元素的位置(初始为1,因第一个元素肯定是存在的)。 count指针 记录当前数字出现的次数。count 的最小计数始终为 1。

  • 遍历数组(从索引 1 开始一次处理一个数组元素)。

    • 若当前元素与前一个元素相同,即 nums[i]==nums[i-1],则 count++。否则,count=1
    • 若 count > 2,则说明遇到了多余的重复项。在这种情况下,我们只向前移动 i(遍历过程中就在移动),而 j 不动。此时,什么都不许做。
    • 若 count <=2,则我们将 i 所指向的元素移动到 j 位置即复制nums[j]=nums[i],并同时增加 i 和 j。
  • 当数组遍历完成,则返回 j。

【python代码实现】

class Solution:
    def removeDuplicates(self, nums: List[int]) -> 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

方法二:
具体思路见下面那一道题

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        def solve(k):
            u = 0
            for x in nums:
                if u < k or nums[u - k] != x:
                    nums[u] = x
                    u += 1
            return u
        return solve(2)

5、删除排序数组中的重复项III (保留K位)

题目描述:

【中等题】
\quad \quad 此为前两题题的进阶版,将原问题的排序数组「保留 2 位」或「保留 1 位」 修改为「保留 k 位」。

对于此类问题,有以下思路:

  • 由于是保留 k 个相同数字,对于前 k 个数字,我们可以直接保留
  • 对于后面的任意数字,能够保留的前提是:与当前写入的位置前面的第 k 个元素进行比较,不相同则保留
def removeDuplicates(nums,k):
    j=0
    for x in nums:
        if j<k or nums[j-k] !=x:
            nums[j]=x
            j+=1
    return j,nums[0:j]

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值