LeetCode数组-移除元素
简介
数组在内存中是连续存放数据的,如果数组为【1,2,3,4,5】,移除元素3,后面所有的元素都会往前移动一位向前覆盖,变为【1,2,4,5,5】,一些编程语言会将最后一个5不输出,但其实内存空间中这个数组还是有五个元素。
库函数如果能一步解决Leetcode的题目的话,建议不要使用,如果作为一系列代码中的一部分使用,且明白内部逻辑,再使用。
.
27题移除元素
题目的要求:
给你一个数组 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。
暴力法
两个for循环,外循环遍历数组移除元素,内循环更新数组完成后面元素的覆盖
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
class Solution {
public:int removeElement(vector<int>& nums, int val) {int size = nums.size();for (int i = 0; i < size; i++) {if (nums[i] == val) { // 发现需要移除的元素,就将数组集体向前移动一位for (int j = i + 1; j < size; j++) {nums[j - 1] = nums[j];}i--; // 因为下标i以后的数值都向前移动了一位,所以i也向前移动一位size--; // 此时数组的大小-1}}return size;}
};
快慢指针双指针法
核心思想移除元素=发现等于val值的数组元素,将后续的元素覆盖到前面
所以是两个操作:发现和覆盖
我们可以定义两个指针,fast和slow
如果我们要覆盖之前的元素,那就是定义一个新数组,我们可以把slow(覆盖)理解为指向新数组的下标,fast和slow都指向同一个数组,fast一直往前探路,发现一个不等于val的数组元素则让slow下标保存下来,如果发现了等于val的数组元素,则slow不动,fast继续++。
这样的话,我们只需要一次循环就可以结束移除元素的操作,其中:
fast的作用是发现要存放在新数组中的元素
slow的作用是指向要存放的元素在新数组中的下标
class Solution {public int removeElement(int[] nums, int val) {//使用双指针//fast为发现不等于val,要存放在新数组中的元素在当前数组的下标//slow为要存放在新数组中的元素下标int slow=0;for(int fast=0;fast<nums.length;fast++){//如果当前fast指向的数组元素不等于VAL,那么就要放在slow下表对应的新数组位置中if(nums[fast]!=val){nums[slow++]=nums[fast];}}//最后slow的下标为最后一个不等于val的新数组元素下标+1,也就是所有存放在新数组中的元素数量return slow;}
}
时间复杂度O(n)
空间复杂度O(1)
26题删除有序数组中的重复项
给你一个 非严格递增排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
- 更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。 - 返回
k
。 -
输入:nums = [0,0,1,1,1,2,2,3,3,4] 输出:5, nums = [0,1,2,3,4]
双指针法
class Solution {public int removeDuplicates(int[] nums) {//第一项nums[slow]和nums[fast]肯定相等,而且有序数组,这个值也肯定作为新数组的第一项,所以slow和fast直接从1开始就可以int slow=1;for(int fast=1;fast<nums.length;fast++){ if(nums[fast]!=nums[slow-1]){//fast寻找和nums[slow-1]不一样的放到新数组中就可以nums[slow++]=nums[fast];}}return slow; }
}
题目中这个数组是递增有序的,所以我们只需要让fast从第二个元素开始寻找和num[slow-1]不一样的元素当做新数组元素num[slow]就可以。
283题移动0
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums =[0,1,0,3,12]
输出:[1,3,12,0,0]
示例 2:
输入: nums =[0]
输出:[0]
双指针法
就拿[0,1,0,3,12]举例,如果我们按照之前移除元素0来做,覆盖完的数组就是[1,3,12,3,12]
所以这题的关键在于在移动非零数字时,要和slow指针所在的元素进行交换,而不是单纯的覆盖。
class Solution {public void moveZeroes(int[] nums) {int slow=0;for(int fast=0;fast<nums.length;fast++){if(nums[fast]!=0){int temp=nums[slow];nums[slow++]=nums[fast];nums[fast]=temp;}}}
}
844题比较含退格的字符串
给定 s
和 t
两个字符串,当它们分别被输入到空白的文本编辑器后,如果两者相等,返回 true
。#
代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
示例 1:
输入:s = "ab#c", t = "ad#c" 输出:true 解释:s 和 t 都会变成 "ac"。
示例 2:
输入:s = "ab##", t = "c#d#" 输出:true 解释:s 和 t 都会变成 ""。
示例 3:
输入:s = "a#c", t = "b" 输出:false 解释:s 会变成 "c",但 t 仍然是 "b"。
提示:
1 <= s.length, t.length <= 200
s
和t
只含有小写字母以及字符'#'
字符串知识补充
- 快慢指针适合对数组操作,将字符串转为字符数组,char[] arr=String.toCharArray();
- 最终操作完的字符数组转为字符串,new String(arr)
- 如果slow处于0的下标位置,意思是已经退格到空字符了,没有其他字符,只要处于1或者以上的位置,肯定有字母,所以可以用substring(0,slow)来处理字符数组转化的字符串
快慢指针法
重点在于字符串的操作,以及如果fast指向了退格号,那我们应该将slow的指针回退,如果slow已经是0了,那就保持不变
class Solution {public String dealString(String st){char[] arr=st.toCharArray();int slow=0;for(int fast=0;fast<arr.length;fast++){if(arr[fast]!='#'){arr[slow++]=arr[fast];}else{//如果已经是空字符了,退格后还是空字符if(slow>0){slow--;}}}return new String(arr).substring(0,slow);}public boolean backspaceCompare(String s, String t) {return dealString(s).equals(dealString(t));}
}