移除元素

力扣27题目链接(opens new window)

给你一个数组 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。

你不需要考虑数组中超出新长度后面的元素

初见思路

输入是一个数组nums和删减目标值val

那么遍历数组我认为是需要的,必须找出待移除的值

然后是如何移除目标值,对于数组来说,里面的值是连续的,所以不能直接就"删除"某个值

实际上我们调用del或者什么方法去删除数组中的值都只是把待删除的值后后面的值覆盖掉了,并没有真正的"删除"数组在该位置的空间,也就是说数组原来的大小是5 ,删除之后其在内存中仍然是5,只是返回的size变成了4

基于此,我们还需要一个循环来覆盖待删除的值

所以,暴力解法需要两层for循环,以下是代码实现

Java版(超时)

class Solution {
public int removeElement(int[] nums, int val) {
int nums_len = nums.length;
for(int i = 0; i < nums.length; i++){
if(nums[i] == val){
for(int j = i + 1; j < nums.length; j++){
nums[j - 1] = nums[j];
}
i -= 1;
nums_len -= 1;
}
}
return nums.length; }
}

这种方法在java版下是超时的,c++版勉强不超时

可见O(n*n)复杂度的代码在刷题时几乎不可用

常规思路

原地移除元素通常会使用双指针(也叫快慢指针)的方法解决

这里的"双指针"的意思得先说清楚

首先,这个“指针”并不是指C/C++中的那个对象,它只是用来代指数组下标的一个变量,因此在任何语言中你都可以实现双指针,这更多的是一种思想在应用双指针时,需要考虑好每个指针的意义

那么,双指针在这里的含义是什么?

这里我们需要一个快指针和一个慢指针

  • 快指针用来在数组中寻找新数组需要的值【其实就是遍历数组】

  • 慢指针则代表着新数组的下标

最开始,快指针和慢指针的位置是一样的(想象一下)

 ↓(快指针)
[1,2,3,4]
↑(慢指针)

假设现在需要删除的数是3

那么我们需要先遍历数组【注意:我们不是要找待删除的数,而是要找构成新数组的数】,逻辑如下

int slow = 0;
for(int fast = 0; fast < nums.length; i++){
if(nums[fast] != 3){
...//做处理
}
}
return slow;

现在遍历开始

==============step1
↓(快指针),1不需要删
[1,2,3,4]
↑(慢指针) 新数组的值:[1]
==============step2
↓(快指针),2不需要删
[1,2,3,4]
↑(慢指针) 新数组的值:[1,2]
==============step3
↓(快指针),3需要删,慢指针不更新
[1,2,3,4]
↑(慢指针) 新数组的值:[1,2]
==============step4
覆盖
↓(快指针),后面的值覆盖3,4不需要删
[1,2,4, ←4]
↑(慢指针) 新数组的值:[1,2,4] ↓(快指针),遍历结束
[1,2,4,4]
↑(慢指针) 新数组的值:[1,2,4]

以上就是过程,如果觉得抽象建议去搜gif来看

解题模板

代码就按照上面的思路来写就好了

c++版
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//定义慢指针
int slow = 0;
for(int fast = 0; fast < nums.size(); ++fast){
if(nums[fast] != val){
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
};
Java版
class Solution {
public int removeElement(int[] nums, int val) {
int slow = 0;//定义慢指针
int nums_len = nums.length;
for(int fast = 0; fast < nums_len; fast++){
if(nums[fast] != val){//遍历并寻找构成新数组可用的数(即除了3以外的),然后添加按新数组下标slow添加到新数组中
nums[slow] = nums[fast];
slow += 1;
} }
return slow;//此时的slow返回的正好是数组大小(不是从0开始的) }
}
Python版
class Solution:
def removeElement(self, nums: List[int], val: int) -> int:
slow = 0 #慢指针
fast = 0 #快指针
nums_len = len(nums)
while fast < nums_len: #这里不可以用枚举方法,所以不用for来遍历数组,用while代替
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow

拓展练习

LeetCode26删除有序数组中的重复项

这题中,快指针仍然是为新数组寻找元素,这些元素的条件是均不相同,判断的方式是在快指针遍历数组的过程中,比较数组相邻两个元素是否相同,不同的即符合要求

慢指针仍为新数组的下标,只有在添加符合要求的数的时候才移动

注意:

  • 不要拿慢指针指向的数与快指针指向的数做比较
  • 数组中第一个数不用判断,因此快慢指针都从1开始
  • 更完美的解法还可以判断一下输入数组的元素是否大于1个

c++版

class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int slow = 1;//慢指针初始化为1,第一个数不用比
for(int fast = 1; fast < nums.size(); ++fast){
if(nums[fast - 1] != nums[fast]){//比较相邻两数是否相等
nums[slow] = nums[fast];//新数组(内容是新的,数组还是原来那个数组)只要不重复的值
slow++;
}
}
return slow;
}
};

Java版

class Solution {
public int removeDuplicates(int[] nums) {
int slow = 1;//第一个数不用判断,因此从1开始
for(int fast = 1; fast < nums.length; fast++){
//快指针遍历数组,若相邻两数不同,则将数添加到新数组(新数组下标仍是慢指针)
if(nums[fast - 1] != nums[fast]){
nums[slow] = nums[fast];//此时fast - 1在数值上和slow一致
slow += 1;
}
}
return slow;
}
}

Python版

class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
slow = 1
fast = 1
num_len = len(nums)
while fast < num_len:
if nums[fast - 1] != nums[fast]:
nums[slow] = nums[fast]
slow += 1
fast += 1
return slow
LeetCode283移动零

这题也有坑,最开始我按照常规思路做:快指针遇到非0的元素,给通过慢指针加到新数组,然后当前快指针处元素赋值0

c++版

class Solution {
public:
void moveZeroes(vector<int>& nums) {
int slow = 0;
if(nums.size() > 1){
for(int fast = 0; fast < nums.size(); ++fast){
if(nums[fast] != 0){
nums[slow] = nums[fast];
slow++;
}
}
//数组修改完毕,此时需要从slow处开始将将后续元素全部置为0,slow的大小肯定小于数组长度
for(int i = slow; i < nums.size(); ++i) nums[i] = 0;
} }
};

注意置0代码写的位置

class Solution {
public void moveZeroes(int[] nums) {
int slow = 0;
int nums_len = nums.length;
if(nums_len > 1){
for(int fast = 0; fast < nums_len; fast++){
if(nums[fast] != 0){
nums[slow] = nums[fast];
nums[fast] = 0;
slow += 1;
}
}
}
}
}

这里问题:没有考虑最开始的值是否为0,这导致有部分初始值不为0的用例无法通过【例如[1,0]】

修改:

不在向新数组添加元素后就将fast处元素置0,而是等到fast将数组遍历一遍之后,从slow处开始,将往后的元素全部置0即可

Java版

class Solution {
public void moveZeroes(int[] nums) {
int slow = 0;
int nums_len = nums.length;
if(nums_len > 1){
for(int fast = 0; fast < nums_len; fast++){
if(nums[fast] != 0){
nums[slow] = nums[fast];
slow += 1;
}
}
for(int i = slow; i< nums_len; i++){
nums[i] = 0;
}
}
}
}

Python版(超时)

class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
slow = 0
fast = 0
num_len = len(nums)
while fast < num_len:
if nums[fast] != 0:
nums[slow] = nums[fast]
slow += 1
fast += 1 while slow < num_len:
nums[slow] = 0

【LeetCode数组#2双指针法】移除元素、删除有序数组中的重复项、移动0的更多相关文章

  1. LeetCode 83. Remove Duplicates from Sorted List (从有序链表中去除重复项)

    Given a sorted linked list, delete all duplicates such that each element appear only once. For examp ...

  2. [LeetCode] 82. Remove Duplicates from Sorted List II 移除有序链表中的重复项 II

    Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numb ...

  3. [LeetCode] Remove Duplicates from Sorted List 移除有序链表中的重复项

    Given a sorted linked list, delete all duplicates such that each element appear only once. For examp ...

  4. [LeetCode] Remove Duplicates from Sorted List II 移除有序链表中的重复项之二

    Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numb ...

  5. [LeetCode] 83. Remove Duplicates from Sorted List 移除有序链表中的重复项

    Given a sorted linked list, delete all duplicates such that each element appear only once. Example 1 ...

  6. [LeetCode] 82. Remove Duplicates from Sorted List II 移除有序链表中的重复项之二

    Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numb ...

  7. LeetCode Remove Duplicates from Sorted List 删除有序链表中的重复结点

    /** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode ...

  8. leetcode83,删除有序链表中的重复元素

    Given a sorted linked list, delete all duplicates such that each element appear only once. For examp ...

  9. 82. Remove Duplicates from Sorted List II(删除有序链表中的重复元素)

    Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numb ...

  10. LeetCode(26): 删除排序数组中的重复项

    Easy! 题目描述: 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度. 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间 ...

随机推荐

  1. 【转帖】在ECS上配置skywalking-nginx-lua

    https://help.aliyun.com/document_detail/197660.html 此处以在CentOS 7.0上的操作为例. 配置Lua运行环境. 安装工具库.   yum in ...

  2. [转帖]SkyWalking集成logback

    1.引入skywalking的jar包,导入的包和agent版本一致 <dependency> <groupId>org.apache.skywalking</group ...

  3. [转帖]一次fork引发的惨案!

    https://www.cnblogs.com/xuanyuan/p/15502289.html "你还有什么要说的吗?没有的话我就要动手了",kill程序最后问道. 这一次,我没 ...

  4. jconsole的简单学习

    摘要 jconsole 是JDK自带的一款图形化监测工具 他可以监测本地程序,也可以检测远程的机器 在没有其他监控手段可以使用的情况下可以快速进行必要的监测 使用方法也比较简单. 本地监控 jcons ...

  5. Linux 下面删除指定日期之前文件的办法

    1. Linux 下面最近有一个需求 需要只更新2020年4月10号之后补丁的需求 2. rsync 能够拉取所有的补丁文件  没找到能够按照日期进行拉取的办法. 所以想了一个折中的办法 先拉取 再按 ...

  6. RabbitMQ原理详解

    RabbitMQ:我们通常谈到消息队列,就会联想到这其中的三者:生产者.消费者和消息队列,生产者将消息发送到消息队列,消费者从消息队列中获取消息进行处理.对于RabbitMQ,它在此基础上做了一层抽象 ...

  7. protojson简介

    google.golang.org/protobuf/encoding/protojson 是 Go 语言中的一个库,用于处理 Protocol Buffers(protobuf)和 JSON 之间的 ...

  8. 为不同版本python安装pip的正确做法

    想学习Python3,但是暂时又离不开Python2.在Windows上如何让它们共存呢? 目前国内网站经常会让大家把其中一个python.exe改个名字(嗯,我也这样讲过,在此纠正一下),这样区分开 ...

  9. MeshFilter mesh vs sharedMesh

    MeshFilter有两个属性mesh和sharedMesh,从官方文档和实际使用来说说这两者的区别 MeshFilter文档 Unity的MeshFilter文档:https://docs.unit ...

  10. 关于git pull机制和游戏开发热更新思考

    前言 今天由于网速很慢,在git pull更新时我观看了git pull的日志,让我联想到和我现在从事的游戏开发中的热更热有一定的相似性,把思绪记录下来. ​ git pull 日志 使用tortoi ...