移除元素

力扣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. [转帖]jumpserver (Linux资产管理快速入门)

    准备工作 准备三台虚拟机,一台作为jumpserver的服务端,两台作为测试端. 一.安装好jump server后,输入IP地址登录 [192.168.2.111为本机测试地址] 二.创建用户组 这 ...

  2. [转帖] Linux命令拾遗-剖析工具

    https://www.cnblogs.com/codelogs/p/16060472.html 简介# 这是Linux命令拾遗系列的第五篇,本篇主要介绍Linux中常用的线程与内存剖析工具,以及更高 ...

  3. CPU 大拿的作品

    http://nieyong.github.io/wiki_cpu/index.html 改天学习写一下.

  4. Redis数据倾斜与JD开源hotkey源码分析揭秘

    1 前言 之前旁边的小伙伴问我热点数据相关问题,在给他粗略地讲解一波redis数据倾斜的案例之后,自己也顺道回顾了一些关于热点数据处理的方法论,同时也想起去年所学习JD开源项目hotkey--专门用来 ...

  5. 在WPF应用中实现DataGrid的分组显示,以及嵌套明细展示效果

    我在前面随笔<在Winform系统开发中,对表格列表中的内容进行分组展示>,介绍了Winform程序中对表格内容进行了分组的展示,在WPF应用中,同样也可以对表格的内容进行分组展示,不过处 ...

  6. Tomcat8安装手记

    Tomcat安装虽然简单,稍不注意,就会坠入万丈深渊,记录痛苦的安装经历. 首先先介绍一下安装条件和正确的安装方式. 安装条件 系统已经安装jdk(前提) tomcat8压缩包 (可以去官网下载 或者 ...

  7. 解决Python报错SSLError,如果试了网上一大堆方法还不行,看看这个吧!!

    前言 这个问题困扰了群友一天,我怀着好奇心去试试看,不到5分钟给解决了哈哈. 报错代码 报错代码中对相关的host和url进行了替换,大家在网上发布内容也要注意隐私哈,多长个心眼子总没错. reque ...

  8. 初步上手Git软件及GitHub平台:基本操作方法

      本文介绍Git软件与GitHub平台的基本内容.使用方法与应用场景等. 目录 1 初步介绍 2 使用方法 2.1 GitHub配置 2.2 Git配置 2.3 代码上传至GitHub 1 初步介绍 ...

  9. 如何使用MBP制作Win启动盘

    最近有一个需求,想给家人的一台笔记本安装一套win 10的操作系统,但是我手头上现在没有对应的启动U盘. 由于工作原因,很多年没用win了,工作电脑也都是MBP,根本没有之前使用win时熟悉的Ultr ...

  10. Windows平台安装Oracle11.2.0.4客户端报错INS-30131

    之前为解决EXP-00003错误给出了安装Oracle11.2.0.4的解决方案,自己测试是没问题的,客户自己安装反馈遇到报错INS-30131,MOS有一篇文章: E1: DB: Error INS ...