ACM 选手图解 LeetCode 移除元素

简介: 给你一个数组 nums 和一个 val,原地移除所有数值等于 val 的值,并返回移除后数组的新长度。

大家好呀,我是帅蛋。


最开始在讲数组的时候并没有写实战题,当时想的是数组这么简单,有啥好写的。


直到最近有蛋粉同我讲,才发现是我自己 xue xue 有些飘了。

image.png能写的明明很多嘛!

image.png

这让我想到了“知识的诅咒”,当一个人知道了某事,就无法想象这件事在未知者眼中的样子


我引以为戒。

image.png

因为数组实在太常用了,我就挑几种类型的题来讲一下,大致是图中读者说的那几个。


以后想起来别的再继续补充。


今天解决移除元素,是快慢指针的经典题目。话不多说,直接开整


image.png

 LeetCode 27:移除元素



题意



给你一个数组 nums 和一个 val,原地移除所有数值等于 val 的值,并返回移除后数组的新长度。



示例


输入: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) 空间复杂度并原地修改输入数组。


  • 0 <= len(nums) <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100


题目解析


水题,题目简单。


这道题相当于数组元素的删除,既然涉及到数组的删除,就不是想当然的删除。


我在文章【ACM 选手带你玩转数组】中讲过:


数组是用连续的一段内存空间,来存储相同数据类型数据的集合。因为这个“连续内存存储”使得数组在插入或者删除的元素的时候,其它元素也要相应的移动。


这道题数组的数据量很小,最多只到 100 个,所以可以用暴力手法破解。


所谓暴力手法就是最无脑的方式,两层 for 循环,第一层 for 循环从 0 开始遍历数组,第二层 for 循环维护后面的元素向前移动。


比如第一层 for 循环遍历到下标为 2 的位置发现 nums[2] == val,那么第二层循环就将 i+1 ~ n -1 位置上的元素全部向前移动一个位置。


因为是双重 for 循环,此时的时间复杂度为 O(n²)。


虽然也能 AC,但显然作为新时代的三好男人,本帅蛋在这方面追求的肯定不是慢,而是秒男的极致,我要快!


image.png


要我快点的话,这就不得不提快慢指针了。


快慢指针,顾名思义,是使用速度不同的指针(可用在链表、数组、序列等上面),来解决一些问题。


快慢指针用 fast 和 slow 两个指针控制,快指针 fast 指向当前要和 val 对比的元素,慢指针 slow 指向将被赋值的位置


  • 遍历数组。
  • 如果 fast 指针指向的元素 nums[fast] != val,则 nums[fast] 是输出数组的元素,将其赋值到 nums[slow] 的位置,slow 和 fast 同时向后移动一位。
  • 如果 nums[fast] == val,证明当前 nums[fast] 是要删除的元素,fast 向后移动一位。
  • fast 遍历完整个数组,结束,slow 的值就是剩余数组的长度。


图解


nums = [0,1,2,2,3,0,4,2], val = 2 为例。


首先初始化 fast 和 slow 指针。

image.png

# 初始化快慢指针
fast = slow = 0

遍历整个数组。


第 1 步,fast = 0,slow = 0,此时 nums[fast] = 0,不等于 val。


此时 nums[slow] = nums[fast](虽然它俩现在本来就是一个),slow 和 fast 向后移动一位。

image.png

# 如果快指针指向的值不等于 val
# fast 指向位置的元素向 slow 指向的位置赋值
if nums[fast] != val:
    nums[slow] = nums[fast]
    slow += 1
    fast += 1

第 2 步,fast = 1,slow = 1,此时 nums[fast] = 1,不等于 val。


此时nums[slow] = nums[fast],slow 和 fast 向后移动一位。

image.png

第 3 步,fast = 2,slow = 2,此时 nums[fast] = 2,等于 val。


此时 fast 向后移动一位,slow 不动。


image.png

# 如果快指针指向的值等于 val
# 只 fast 指针移动,不向 slow 指针指向的位置赋值
fast += 1

第 4 步,fast = 3,slow = 2,此时 nums[fast] 依然是 2等于 val。


所以还是 fast 向后移动一位,slow 不动。


image.png

第 5 步,fast = 4,slow = 2,此时 nums[fast] = 3,不等于 val。


此时nums[slow] = nums[fast] = 3,slow 和 fast 向后移动一位。

image.png

第 6 步,fast = 5,slow = 3,nums[fast] = 0不等于 val。


此时nums[slow] = nums[fast] = 0,slow 和 fast 向后移动一位。

image.png

第 7 步,fast = 6,slow = 4,nums[fast] = 4,不等于 val。


此时nums[slow] = nums[fast] = 4,slow 和 fast 向后移动一位。

image.png

第 8 步,fast = 7,slow = 5,nums[fast] = 2,和 val 相等。


此时 fast 向后移动一位,slow 不变,数组遍历结束。


可以看出,此时从 [0, slow) 为剩下的输出数组,slow 的值为剩下数组的长度。


本道题的解法,使用了一层 for 循环,所以时间复杂度为 O(n)


额外使用了两个指针 fast 和 slow,空间复杂度为 O(1)


代码实现


Python 代码实现

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        if len(nums) == 0:
            return 0
        # 初始化快慢指针
        fast = slow = 0
        while fast < len(nums):
            # 如果快指针指向的值不等于 val
            # fast 指向位置的元素向 slow 指向的位置赋值
            if nums[fast] != val:
                nums[slow] = nums[fast]
                slow += 1
            # 如果快指针指向的值等于 val
            # 只 fast 指针移动,不向 slow 指针指向的位置赋值
            fast += 1
        return slow

Java 代码实现

class Solution {
    public int removeElement(int[] nums, int val) {
        // 初始化快慢指针
        int fast = 0;
        int slow = 0;
        for (; fast < nums.length; fast++) {
            if (nums[fast] != val) {
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
}

图解移除元素到这就结束辣,数组实战第一篇文章,是不是很有感觉?


这只是一道开胃菜,精彩的题目还在后面呐。


大家记得帮我点赞+在看呀,让我麻溜肝起来!


我是帅蛋,我们下次见!

相关文章
|
1月前
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
35 1
|
1月前
【LeetCode 27】347.前k个高频元素
【LeetCode 27】347.前k个高频元素
33 0
|
1月前
【LeetCode 06】203.移除链表元素
【LeetCode 06】203.移除链表元素
30 0
|
1月前
【LeetCode-每日一题】移除元素
【LeetCode-每日一题】移除元素
31 0
|
3月前
|
存储 算法
LeetCode第83题删除排序链表中的重复元素
文章介绍了LeetCode第83题"删除排序链表中的重复元素"的解法,使用双指针技术在原链表上原地删除重复元素,提供了一种时间和空间效率都较高的解决方案。
LeetCode第83题删除排序链表中的重复元素
|
3月前
|
算法 索引
LeetCode第34题在排序数组中查找元素的第一个和最后一个位置
这篇文章介绍了LeetCode第34题"在排序数组中查找元素的第一个和最后一个位置"的解题方法,通过使用双指针法从数组两端向中间同时查找目标值,有效地找到了目标值的首次和最后一次出现的索引位置。
LeetCode第34题在排序数组中查找元素的第一个和最后一个位置
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer II 082. 含有重复元素集合的组合
解决LeetCode平台《剑指 Offer II 082. 含有重复元素集合的组合》题目的Python代码实现,通过深度优先搜索算法找出所有和为特定目标值的数字组合,并在搜索过程中通过排序和跳过重复元素来避免解集中出现重复组合。
40 2
|
3月前
|
算法
LeetCode第27题移除元素
这篇文章介绍了LeetCode第27题"移除元素"的解题方法,通过使用双指针技巧,有效移除数组中特定值的元素并返回新数组的长度。
|
3月前
|
算法 索引 Python
【Leetcode刷题Python】34. 在排序数组中查找元素的第一个和最后一个位置(二分查找)
解决LeetCode "在排序数组中查找元素的第一个和最后一个位置" 问题的方法。第一种方法是使用两次二分查找,首先找到目标值的最左边界,然后找到最右边界。第二种方法是利用Python的list.index()方法,先正序找到起始位置,再逆序找到结束位置,并给出了两种方法的Python实现代码。
62 0
|
3月前
|
Python
【Leetcode刷题Python】203.移除链表元素
文章提供了三种删除链表中特定值节点的方法:迭代法、虚拟节点迭代删除法和递归法,并给出了相应的Python实现代码。
24 0