给你一个数组 nums
和一个值 val
,你需要 原地 移除所有数值等于 val
的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1)
额外空间并 原地** 修改输入数组**。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
必须使用 O(1) 空间复杂度并原地修改输入数组。O(1)表示所需空间为常量,并且与n无关
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
i = len(nums)
for k, v in enumerate(nums):
if v == val:
i -= 1
a = v
for j in range(k,len(nums)-1):
nums[j] = nums[j+1]
nums[len(nums)-1] = a
return i
评价: 因为是双重 for 循环,此时的时间复杂度为 O(n²)。
### 方法一:双指针
#### 思路:
快慢指针,顾名思义,是使用速度不同的指针(可用在链表、数组、序列等上面),来解决一些问题。**
快慢指针用 fast 和 slow 两个指针控制,**快指针 fast 指向当前要和 val 对比的元素,慢指针 slow 指向将被赋值的位置**:
- 遍历数组。
- 如果 fast 指针指向的元素 nums[fast] != val,则 nums[fast] 是输出数组的元素,将其赋值到 nums[slow] 的位置,slow 和 fast 同时向后移动一位。
- 如果 nums[fast] == val,证明当前 nums[fast] 是要删除的元素,fast 向后移动一位。
- fast 遍历完整个数组,结束,slow 的值就是剩余数组的长度。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
if len(nums) == 0:
return 0
slow = fast = 0
while fast < len(nums):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
#### 复杂度分析
时间复杂度:O(n),其中 n为序列的长度。我们只需要遍历该序列至多两次。
空间复杂度:O(1)。我们只需要常数的空间保存若干变量。
#### 思路:
如果要移除的元素恰好在数组的开头,例如序列 [1,2,3,4,5][1,2,3,4,5],当val 为 1 时,我们需要把每一个元素都左移一位。注意到题目中说:「元素的顺序可以改变」。实际上我们可以直接将最后一个元素 5 移动到序列开头,取代元素 1,得到序列 [5,2,3,4],同样满足题目要求。这个优化在序列中 val 元素的数量较少时非常有效。
实现方面,我们依然使用双指针,两个指针初始时分别位于数组的首尾,向中间移动遍历该序列
#### 算法:
如果左指针left 指向的元素等于 val,此时将右指针right 指向的元素复制到左指针left 的位置,然后右指针 right 左移一位。如果赋值过来的元素恰好也等于val,可以继续把右指针 right 指向的元素的值赋值过来(左指针 left 指向的等于 val 的元素的位置继续被覆盖),直到左指针指向的元素的值不等于val 为止。
当左指针 left 和右指针 right 重合的时候,左右指针遍历完数组中所有的元素。
这样的方法两个指针在最坏的情况下合起来只遍历了数组一次。与方法一不同的是,方法二避免了需要保留的元素的重复赋值操作。
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
left = 0
right = len(nums)-1
while left <= right:
if nums[left] == val:
nums[left] = nums[right]
right -= 1
else:
left += 1
return left
#### 根据思路和算法写代码出现的问题!!:
while left != right:如果用!=或<,会遗漏一个元素(left==right时)!严谨一点,可以再优化为以下代码,**避免**当指针left遍历完最后一个元素后,指针left在指针right右边:
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
left = 0
right = len(nums)
while left < right:
if nums[left] == val:
nums[left] = nums[right-1]
right -= 1
else:
left += 1
return left
#### 复杂度分析
- 时间复杂度:O(n),其中 nn 为序列的长度。我们只需要遍历该序列至多一次。
- 空间复杂度:O(1)。我们只需要常数的空间保存若干变量。
引用:
1.ACM 选手图解 LeetCode 移除元素:https://mp.weixin.qq.com/s?__biz=MzI0NjAxMDU5NA==&mid=2475922234&idx=1&sn=5f0a3fce40927c80ff5608da90b6fcd0&chksm=ff22f4b7c8557da10186decdfd4b1f57277b44e1f452388be2ef407bd5e91f8b2e24c624fceb&token=1145569493&lang=zh_CN#rd
2.:力扣(LeetCode)https://leetcode.cn/problems/remove-element/solution/yi-chu-yuan-su-by-leetcode-solution-svxi/