移除元素

力扣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. [转帖]shell命令替换~date用法~如果被替换命令的输出内容包括多行或有多个连续的空白符,输出变量时应该将变量用双引号包围

    https://www.cnblogs.com/mianbaoshu/p/12069458.html Shell 命令替换是指将命令的输出结果赋值给某个变量.比如,将使用ls命令查看到的某个目录中的内 ...

  2. [转帖]学习linux必须知道的命令

    https://www.cnblogs.com/aibeier/p/15315487.html 基础不牢,地动山摇.在linux命令行下查看命令帮助man用于查看命令的帮助信息 man cp--hel ...

  3. [转帖]linux下如何避免rsyslog系统日志不停打印到console

    背景:linux环境下,服务器由于某种异常导致rsyslog message不停打印到console控制台,影响我们正常使用. ps:我遇见的场景: 解决办法:1. vim /etc/rsyslog. ...

  4. 简单的获取ESXi服务器上面开启了多少个vCPU的办法

    开启ssh 执行命令 esxcli vm process list |grep Config |cut -b 17- |xargs cat |grep numvcpus |cut -d " ...

  5. Docker容器基础入门认知-Cgroup

    在上一篇说完 namespace 给容器技术提供了隔离之后,我们在介绍一下容器的"限制"问题 也许你会好奇,我们不是已经通过 Linux Namespace 给容器创建了一个容器了 ...

  6. 【解决一个小问题】macbook m2 上交叉编译 gozstd

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 已知 zstd 是一个优秀的压缩库,gozstd封装了这个 ...

  7. c++基础之语句

    上一次总结了一下c++中表达式的相关内容,这篇博文主要总结语句的基础内容 简单语句 c++ 中语句主要是以分号作为结束符的,最简单的语句是一个空语句,空语句主要用于,语法上需要某个地方,但是逻辑上不需 ...

  8. GoodSync(最好的文件同步软件)

    GoodSync是一款使用创新的最好的文件同步软件,可以在你的台式机.笔记本.USB外置驱动器等设备直接进行数据文件同步软件. GoodSync将高度稳定的可靠性和极其简单的易用性完美结合起来,可以实 ...

  9. 1.29 深痛教训 关于 unsigned

    unsigned long long 无符号长长整型,常用于比 long long 大一倍的整数范围或自然溢出 \(\bmod 2^{64}\) unsigned long long 范围为 \(0\ ...

  10. 【动态内存】C语言动态内存使用常见错误及其避免方法(初学者避雷)

    C语言动态内存使用常见错误及其避免方法(初学者动态内存避雷手册) 求个赞求个赞求个赞求个赞 谢谢 先赞后看好习惯 打字不容易,这都是很用心做的,希望得到支持你 大家的点赞和支持对于我来说是一种非常重要 ...