删除元素-双指针法
基本思路
如果想要删除一个元素,那么需要将它后面的元素全部往前移动一位,最直接的想法当然是从某一位开始直接逐步将后续元素全部往前移,获得O(n^2)的计算复杂度;如果需要删除的元素较多,那么理论上我们可以像插入排序一样,先记住有哪些要被删掉,然后只做一次向前移动的操作,这用双指针法可以办到。
#include<iostream>
#include<vector>
using namespace std;
//双指针法移除某个特定元素
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
int main(){
vector<int> nums = {1,1,1,2,2}; // 输入数组
vector<int> expectedNums = {1,2,3,4,5,6}; // 长度正确的期望答案
int k = removeElement(nums,1);
cout<<k<<endl;
for (int i = 0; i < k; i++) {
cout<<nums[i] <<" ";
}
}
同类题目有:
/*
283. 移动零
已解答
简单
相关标签
相关企业
提示
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
*/
#include<iostream>
#include<vector>
using namespace std;
//双指针法移除某个特定元素
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
int moveZeroes(vector<int>& nums) {
int len=removeElement(nums,0);
for(int i=len;i<nums.size();i++){
nums[i]=0;
}
return len;
}
int main(){
vector<int> nums = {1,0,0,0,1,1,2,2}; // 输入数组
vector<int> expectedNums = {1,2,3,4,5,6}; // 长度正确的期望答案
int k = moveZeroes(nums);
cout<<k<<endl;
for (int i = 0; i < nums.size(); i++) {
cout<<nums[i] <<" ";
}
}
变式
被删除元素的检查条件
/*
给你一个 非严格递增排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。
考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:
更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
返回 k 。
判题标准:
系统会用下面的代码来测试你的题解:
int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用
assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}
如果所有断言都通过,那么您的题解将被 通过。
示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例 2:
输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。
*/
//双指针法移除某个特定元素
#include<iostream>
#include<set>
#include<vector>
using namespace std;
int removeElement(vector<int>& nums, int val,int nowIndex,int maxIndex) {
int slowIndex = nowIndex;
int fastIndex = nowIndex;
//因为多次删除的情况下,数组最大长度是变化的,所以有maxindex这个修改
for (; fastIndex < maxIndex; fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
int removeDuplicates(vector<int>& nums) {
set<int> s;
int k=nums.size();//因为多次删除的情况下,数组最大长度是变化的,所以有maxindex这个修改
for(int i=0;i<nums.size();i++){
//第一次遇到某个数,记录并对其后续执行降重
if(s.find(nums[i])==s.end()){
k=removeElement(nums,nums[i],i+1,k);
s.insert(nums[i]);
}
}
return k;
}
int main(){
vector<int> nums = {1,1,1,2}; // 输入数组
vector<int> expectedNums = {1,2,3,4,5,6}; // 长度正确的期望答案
int k = removeDuplicates(nums); // 调用
cout<<k<<endl;
for (int i = 0; i < k; i++) {
cout<<nums[i] <<" ";
}
}
根据检测条件移动双指针慢指针的效果
#include<iostream>
#include<vector>
#include<string>
using namespace std;
//双指针法移除某个特定元素
int removeElement(vector<char>& nums, char val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
int removeBackSpace(vector<char>& nums, char val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}else{
slowIndex--;
}
/*
for(int i=0;i<nums.size();i++){
cout<<nums[i]<<" ";
}
cout<<endl;
*/
}
return slowIndex;
}
bool backspaceCompare(string s, string t) {
int slowIndex = 0;
char val='#';
for (int fastIndex = 0; fastIndex < s.size(); fastIndex++) {
if (val != s[fastIndex]) {
s[slowIndex++] = s[fastIndex];
}else{
if(slowIndex!=0)slowIndex--;
}
}
int slow_1=slowIndex;
slowIndex = 0;
for (int fastIndex = 0; fastIndex < t.size(); fastIndex++) {
if (val != t[fastIndex]) {
t[slowIndex++] = t[fastIndex];
}else{
if(slowIndex!=0)slowIndex--;
}
}
if(slow_1 != slowIndex) return false;
for(int i=0;i<slow_1;i++){
if(s[i]!=t[i]){
return false;
}
}
return true;
}
int main(){
cout<<backspaceCompare("ab#","ac#");
}
可能应用
- 日志系统 背景:许多应用程序和系统都会生成大量的日志数据,这些日志数据通常以数组形式存储在内存中以便快速访问和处理。
需求:为了保持日志系统的性能,定期清理旧的或不再需要的日志条目是必要的。这种清理操作通常需要原地删除大段的日志数据。
- 实时数据流处理 背景:在实时数据流处理系统中,数据通常以数组形式存储在缓冲区中,以便快速处理和分析。
需求:当缓冲区达到一定大小时,需要清理处理过的数据,以腾出空间存储新的数据。这种操作通常需要原地删除大量的已处理数据。
- 游戏开发 背景:游戏中的许多数据(如对象位置、状态等)都存储在数组中,以便快速访问和更新。
需求:在游戏运行过程中,需要频繁删除已不再需要的对象(如已被销毁的敌人、过期的物品等)。这些删除操作需要高效地在原地进行,以保证游戏的性能。
- 数据缓存系统 背景:数据缓存系统中,缓存的数据通常以数组形式存储,以便快速读取和写入。
需求:为了保持缓存的有效性,需要定期删除过期或不再需要的缓存数据。这种操作通常需要在原地高效完成。
- 文本编辑器 背景:文本编辑器中,文本数据通常以数组形式存储,以便快速访问和编辑。
需求:用户在编辑过程中可能会删除大段的文本内容,这需要原地高效地删除数组中的相应元素。