·跟着代码随想录刷力扣· ·数组部分· 3. 双指针法

  • 给你一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,并返回移除后数组的新长度。
  • 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
  • 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。

示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

力扣题目链接:27 移除元素icon-default.png?t=N7T8https://leetcode.cn/problems/remove-element/

1. 暴力解法:直接两个循环

第一个循环:找到目标值i

第二个循环:更新数组,从第i个值开始,a[i]=a[i+1](考虑 或是否要用“修改”一词,但最终采用更新,原因是数组无法被修改,只能被覆盖数据基本理论

需要注意两个点:

1.   for j in range(i, length - 1) : nums[j] = nums[j + 1]处

  • 最开始写的 range(i, length),是错的
  • 原因是, 当j = length - 1时,nums[j + 1]没有意义

2.   i -= 1     最开始没写

  • ## 这个尤其要注意,很容易忽略 ##
  • 写它是因为删除一个数后,由于下标 i 以后的数值都向前移动了一位,所以i也向前移动一位,使得while循环中进行 i + 1操作后,得到的nums[i],是原本没删除时应该要搜索的后一位数
     
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        i = 0
        length = len(nums)
        while i < length:
            if nums[i] == val:
                # 最开始写的 range(i, length),是错的
                # 原因是, 当j = length - 1时,nums[j + 1]没有意义
                for j in range(i, length - 1):
                    nums[j] = nums[j + 1]
                length -= 1   # 因为删掉了一个数,所以数组长度要-1
                ## 这个尤其要注意,很容易忽略 ##
                i -= 1        # // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位
            i += 1
        return length

2. 快慢双指针法:

快慢指针法: 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作

定义快慢指针

  • 快指针:寻找新数组的元素 ,新数组就是不含有目标元素的数组
  • 慢指针:指向更新 新数组下标的位置

算法逻辑:

  • 当未寻找到目标值时,即nums[fast] != val, nums[slow] == nums[fast],两个指针齐头并进,
  • 一旦找到目标值,slow指针不前进,fast指针前进
  • 这样fast遇到下一个非目标值a时,就可使得nums[slow] = a,直接覆盖掉那个目标值,从此以后快慢指针间隔一个位数同时前进
  • 因此快慢指针间隔的位数就是覆盖掉的目标值个数,而慢指针的位置就是剩余元素的个数

注意两个点:

1. while fast < size, 不加等于是因为,fast  = size 时,nums[fast ] 会越界

2. 返回slow不是slow + 1,是因为它指向的是最后一个非要移除元素之后的位置

我当时考虑到slow + 1是因为数组的下标从0开始,数组的个数 = 最后一个数的下标 + 1

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

3. 相向双指针法

相向双指针法: 通过一个头指针和尾指针在一个for循环下完成两个for循环的工作

定义头尾指针

  • 头指针:寻找新数组的元素 ,同时替换目标元素
  • 尾指针:寻找可以被替换的数据

算法逻辑:

  • 当头尾指针未相碰时,执行外层大循环
  • 第一层内部循环:头指针遇到目标对象则跳出循环,等待执行if语句
  • 第二层内部循环:尾指针如果遇到不是目标值的对象,则跳出循环,等待下面if语句执行;如果遇到目标对象的值,则向左移动,这样就可以覆盖掉尾部的目标值,知道遇到下一个非目标值
  • if语句就是将尾指针遇到的非目标值赋给头指针遇到的目标值,从而替换

需要注意的点:

1. 还是 <、<= 的问题

这里到底怎么用其实跟二分法大差不差,具体可以看这一篇:二分法

这里用小于等于,是因为right的初始值为 n-1,left <= right有意义

2. 最后跳出大循环时,由于if语句,left += 1,会指向剩余所有元素的后一位,因此return left即可,不需要left + 1

3. 两个内层循环中的 left <= right 条件是必要的,如果没有,当处理完目标值后,left不会跳出循环而会一直增加,从而跳出数组边界而后报错

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        n = len(nums)
        left = 0
        right = n - 1
        while left <= right:
            while left <= right and nums[left] != val:
                left += 1
            while left <= right and nums[right] == val:
                right -= 1
            if left < right:
                nums[left] = nums[right]
                left += 1
                right -= 1
        return left
      

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值