当前位置: 首页 > news >正文

单序列双指针---初阶篇

目录

相向双指针

344. 反转字符串

125. 验证回文串

1750. 删除字符串两端相同字符后的最短长度

2105. 给植物浇水 II

977. 有序数组的平方

658. 找到 K 个最接近的元素

1471. 数组中的 k 个最强值

167. 两数之和 II - 输入有序数组

633. 平方数之和

2824. 统计和小于目标的下标对数目

2563. 统计公平数对的数目

LCP 28. 采购方案

15. 三数之和

16. 最接近的三数之和

18. 四数之和

611. 有效三角形的个数

1577. 数的平方等于两数乘积的方法数

923. 三数之和的多种可能

948. 令牌放置

11. 盛最多水的容器

42. 接雨水

1616. 分割两个字符串得到回文串

同向双指针

611. 有效三角形的个数

背向双指针

1793. 好子数组的最大分数

原地修改

27. 移除元素

26. 删除有序数组中的重复项

80. 删除有序数组中的重复项 II

283. 移动零

905. 按奇偶排序数组

922. 按奇偶排序数组 II

3467. 将数组按照奇偶性转化

2460. 对数组执行操作

1089. 复写零

总结


本文对双指针算法在面试中的常见应用进行了详细分类和解析,帮助读者掌握双指针的解题技巧和底层逻辑。双指针可以分为相向双指针、同向双指针和背向双指针,分别适用于不同的问题场景,一下将一一列举。通过本文的学习,帮助读者可以更好地理解双指针的核心思想,并灵活运用于实际面试中。PS:本篇博客中的所有题目均来自于灵茶山艾府 - 力扣(LeetCode)分享的题单。

相向双指针

与滑动窗口相反,滑动窗口是同向双指针,left和right都向右或向左移动;相向双指针指的是两个指针都向中间进行移动。下面借助题目进行理解和剖析。

344. 反转字符串

题解:经典的现象双指针问题,使用前后指针进行字符串的交换即可。

125. 验证回文串

题解:依旧是使用相向双指针,但是要注意跳过非字母数字字符,还要注意前后字符进行比较的时候是不区分大小写的。

class Solution {
public:bool isPalindrome(string s) {//依旧是使用同向双指针来进行实现int left=0,right=s.size()-1;while(left<right){while(right>left&&(s[right]<'a'||s[right]>'z')&&(s[right]<'A'||s[right]>'Z')&&(s[right]<'0'||s[right]>'9')) right--;while(right>left&&(s[left]<'a'||s[left]>'z')&&(s[left]<'A'||s[left]>'Z')&&(s[left]<'0'||s[left]>'9')) left++;if(tolower(s[left])!=tolower(s[right])) return false;left++,right--;}return true;}
};

1750. 删除字符串两端相同字符后的最短长度

 题解:如果左右两边字符相同进行循环删除接口,当左右两边字符不同是停止,返回字符串长度。

class Solution {
public:int minimumLength(string s) {//使用指针找到前后字符不同的位置后停止,再通过right和left来确定长度int left=0,right=s.size()-1;while(left<right){if(s[left]!=s[right]) break;   //左右两边字符不同break,进行返回char same=s[left];//一下循环条件应该更改为left<=right解决出现s执行没有字符的情况while(left<=right&&same==s[right]) right--;  //出右边相同字符while(left<=right&&same==s[left]) left++;    //出左边相同字符}return right-left+1;}
};

2105. 给植物浇水 II

题解:让左右两边同时进行浇水,当水不足的时候补水;注意:相遇位置谁来浇水。 

class Solution {
public:int minimumRefill(vector<int>& plants, int capacityA, int capacityB) {//依旧是使用相向双指针进行实现int n=plants.size();int left=0,right=n-1;int ret=0;int ahave=capacityA,bhave=capacityB;while(left<right){if(ahave<plants[left]) //A浇水{ahave=capacityA;ret++;}ahave-=plants[left++];if(bhave<plants[right]) //B浇水{bhave=capacityB;ret++;}bhave-=plants[right--];}if(n%2==1&&max(ahave,bhave)<plants[left]) ret++;  //对于奇数个植物需要对相交位置进行判断return ret; }
};

977. 有序数组的平方

题解:对负数和整数进行拆分,以cur1为分界线,则[0,cur1]内的平方是呈现递减的趋势,[cur1+1,n-1]内的平方是呈现递增的趋势。就转化为了合并两个有序数组。

class Solution {
public:vector<int> sortedSquares(vector<int>& nums) {int n=nums.size();int cur1=0;while(cur1<n&&nums[cur1]<0) cur1++;cur1--;  //cur1向前走   从[0,cur1]的平方是降序的int cur2=cur1+1;  //cur2向后走  从[cur2,n-1]的平方是升序的vector<int> ret(n);int k=0;while(cur1>=0&&cur2<n){if(abs(nums[cur1])<abs(nums[cur2])) ret[k++]=nums[cur1]*nums[cur1--];else ret[k++]=nums[cur2]*nums[cur2++];}while(cur1>=0) ret[k++]=nums[cur1]*nums[cur1--];  //判断cur1是否走完了while(cur2<n) ret[k++]=nums[cur2]*nums[cur2++];return ret;}
};

658. 找到 K 个最接近的元素

题解:先找到最接近x的位置,然后再从该位置分别向前,向后取元素。最终确定区间的位置。

class Solution {
public:vector<int> findClosestElements(vector<int>& arr, int k, int x) {//先找到数组中最接近x的位置int n=arr.size();int pos=0;int near=INT_MAX;  //用pos位置来记录最接近x的位置for(int i=0;i<n;i++) {if(near>abs(arr[i]-x)){near=abs(arr[i]-x);pos=i;}}//此时的pos位置就是最接近x的位置int cur1=pos,cur2=pos+1;  //将cur1向前走,cur2向后走,进行一次判断去哪一边的元素while(k&&cur1>=0&&cur2<n){int left=abs(arr[cur1]-x);int right=abs(arr[cur2]-x);if(left>right) cur2++; //取右边的else cur1--;          //取左边的k--;}while(k&&cur1>=0) cur1--,k--;   //防止k还没有完while(k&&cur2<n) cur2++,k--;vector<int> ret(arr.begin()+cur1+1,arr.begin()+cur2);return ret;}
};

1471. 数组中的 k 个最强值

题解:先对数组进行排序找到中间值。因为数组是排序好的,所以左右两边的值是距离中间值最远的,也就是最有可能成为最强值的数,再对左右进行比较确定最强值。

class Solution {
public:vector<int> getStrongest(vector<int>& arr, int k) {int n=arr.size();sort(arr.begin(),arr.end());   //先进行排序找中间值int m=arr[(n-1)/2];//左右两边一定比中间强,所以控制[left,right]区间,从左右两边找最强值vector<int> ret;int left=0,right=n-1;while(k&&left<=right){int lnum=abs(arr[left]-m);int rnum=abs(arr[right]-m);if(lnum>rnum)   //左边大,入左边{ret.push_back(arr[left]);left++;}else   //右边大,入右边{ret.push_back(arr[right]);right--;}k--;}return ret;}
};

167. 两数之和 II - 输入有序数组

题解:标准的同向双指针的题目,通过左右两边的最大值和最小值来进行缩小和扩大范围直到找到合适的下标组合。

class Solution {
public:vector<int> twoSum(vector<int>& numbers, int target) {//使用相向双指针来实现,当左右两边之和小的时候就让left++//当左右两数之和大的时候让right--//相等的时候就返回int left=0,right=numbers.size()-1;while(left<right){if(numbers[left]+numbers[right]==target) return {left+1,right+1};  //注意题目中下标是从1开始的else if(numbers[left]+numbers[right]>target) right--;else left++;}return {};}
};

633. 平方数之和

题解:b一定是小于等于sqrt(c)的,所以可以直接确定左右区间使用相向双指针来找合适的a和b即可。

class Solution {
public:bool judgeSquareSum(int c) {int left=0,right=sqrt(c);  //right一定是小于等于c的算数平方根的//所以确定左右边界,通过左右两边的平方和确定区间的移动while(left<=right){long long the=left*left;  long long other=right*right;if(the+other==c) return true;else if(the+other>c) right--;else left++;}return false;}
};

2824. 统计和小于目标的下标对数目

方法一:对数组进行排序,right从后向前,依次找nums[right]组合满足条件的数目,将这些数进行相加即可;满足nums[right]+nums[left]>=target的最小left就是nums[right]可以组合的个数。

class Solution {
public:int countPairs(vector<int>& nums, int target) {sort(nums.begin(),nums.end());int ret=0,n=nums.size();for(int i=n-1;i>0;i--){//使用islower_bound来进行临界条件的查找int r=lower_bound(nums.begin(),nums.begin()+i,target-nums[i])-nums.begin();if(r!=n) ret+=r;}return ret;}
};

方法二:将数组进行排序后,使用相向双指针进行统计;当nums[left]+nums[right]<target的时候nums[left]就可以与[left+1,right]区间的所有数进行配对,ret+=right-left,当前left就完成了对所有数的匹配,left++;当nums[left]+nums[right]>=target时,nums[right]就不能与[left,right]的所有数进行匹配,当前的right就完成了对所有数的匹配,right--。

class Solution {
public:int countPairs(vector<int>& nums, int target) {sort(nums.begin(),nums.end());int left=0,right=nums.size()-1;int ret=0;while(left<right){if(nums[left]+nums[right]<target){ret+=right-left;left++;}else right--;}return ret;}
};

2563. 统计公平数对的数目

题解:与上一题的方法二相似也是使用有序的性质将数对的选择进行剪枝;使用相向双指针进行,当nums[left]+nums[right]>upper时说明此时的right没有与之匹配的left了,将right--;当nums[left]+nums[right]<lower时说明此时的left没有与之匹配的right了,将left++;当在有效区间以内的时候,可以固定right求left满足的个数,也可以固定left求right满足条件的个数。

class Solution {
public:long long countFairPairs(vector<int>& nums, int lower, int upper) {int n=nums.size();sort(nums.begin(),nums.end());  //先进行排序,此时的left和right有序方便对不需要组合进行剪枝int left=0,right=n-1;long long ans=0;while(left<right){if(nums[left]+nums[right]>upper) right--;  //当left+right对应的值大于upper时right就没有匹配的left了else if(nums[left]+nums[right]<lower) left++;  //当left+right对应的值小于lower时left就没有匹配的right了else{//此时的nums[left]+nums[right]是满足题意的,所以可以固定right,将找第一个left不满足条件的位置int end=upper_bound(nums.begin()+left,nums.begin()+right,upper-nums[right])-nums.begin()-left;ans+=end;right--;}}return ans;}
};

LCP 28. 采购方案

题解:与两题一样,还是通过有序的数组对进行剪枝。

class Solution {
public:int purchasePlans(vector<int>& nums, int target) {int n=nums.size();sort(nums.begin(),nums.end());  //先进行排序int left=0,right=n-1;int ret=0;while(left<right){if(nums[left]+nums[right]>target) right--;  //此时right的值不能与[left,right-1]任意一个值进行配对else {ret=(ret+right-left)%1000000007;left++;}}return ret;}
};

15. 三数之和

题解:将三数求和------->两数求和,枚举数组的每一个位置找另外两个数即可。找另外两个数依旧是使用相向双指针进行。

class Solution {
public:vector<vector<int>> threeSum(vector<int>& nums) {int n=nums.size();sort(nums.begin(),nums.end());  //进行排序vector<vector<int>> ret;if(nums[0]>0||nums[n-1]<0) return ret;  //当最大值<0以及最小值>0不可能存在满足的数组for(int i=0;i<n-2;i++){if(i>0&&nums[i]==nums[i-1]) continue;    //当前数与上一个就不需要对当前数进行处理,//否则三元组会重复if(nums[i]+nums[i+1]+nums[i+2]>0) break;   //当最小的三个数之和>0没有三元组了if(nums[i]+nums[n-1]+nums[n-2]<0) continue;   //当最小的数与最大的两个数相加<0,当前的i与任何数都不能进行匹配int left=i+1,right=n-1;   //进行两数之和while(left<right){if(nums[left]+nums[right]+nums[i]>0) right--;       else if(nums[left]+nums[right]+nums[i]<0) left++;else {ret.push_back({nums[left],nums[right],nums[i]});left++;while(left<right&&nums[left]==nums[left-1]) left++;  //跳过重复的数据right--;while(left<right&&nums[right]==nums[right+1]) right--;  }}}return ret;}
};

16. 最接近的三数之和

题解:此题与上一题一样,依旧是枚举每一个元素进行三数之和求最接近的位置即可。

class Solution {
public:int threeSumClosest(vector<int>& nums, int target) {//与三数之和类似,还是枚举每一个位置,用同向双指针进行查找最接近的位置int n=nums.size();sort(nums.begin(),nums.end());long long ret=INT_MAX;for(int i=0;i<n-2;i++){//进行同向双指针让和靠近targetint left=i+1,right=n-1;while(left<right){int differ=nums[i]+nums[left]+nums[right];   //求此时的三数之和if(abs(differ-target)<abs(ret-target)) ret=differ;   //判断时候需要进行更新答案if(nums[i]+nums[left]+nums[right]>target) right--;   //此时三数>target,让right--使其靠近targetelse if(nums[i]+nums[left]+nums[right]<target) left++;      //此时三数<target,让left++使其靠近targetelse return target;   //相等就是最接近的位置,直接进行返回}}return ret;}
};

18. 四数之和

题解:与三数求和类似,将四数求和----->三数求和----->两数求和。

class Solution {
public:vector<vector<int>> fourSum(vector<int>& nums, int target) {//将四数之和----->三数之和------>两数之和int n=nums.size();sort(nums.begin(),nums.end());vector<vector<int>> ret;for(int i=0;i<n-3;i++){if(i&&nums[i]==nums[i-1]) continue;  //防止相同数据进行重复插入for(int j=i+1;j<n;j++){if(j!=i+1&&nums[j]==nums[j-1]) continue;   //防止相同数据进行重复插入int left=j+1,right=n-1;while(left<right){//此处的四个数进行相加可能越界,将int强转为long long防止越界if((long long)nums[i]+nums[j]+nums[left]+nums[right]>target) right--;else if((long long)nums[i]+nums[j]+nums[left]+nums[right]<target) left++;else {ret.push_back({nums[i],nums[j],nums[left],nums[right]});left++;while(left<right&&nums[left]==nums[left-1]) left++;right--;while(left<right&&nums[right]==nums[right+1]) right--;}}}}return ret;}
};

611. 有效三角形的个数

题解:与三数求和类似,此题可以通过先确定一边(最大值或最小值),再通过同向双指针找能够组成三角形的组合即可。当nums[min]+nums[mid]>nums[max]时就一定能够组成三角形。

class Solution {
public:int triangleNumber(vector<int>& nums) {//类似与三数求和,通过固定左右一边,再通过同向双指针找合适的位置int n=nums.size(),ret=0;sort(nums.begin(),nums.end());for(int i=n-1;i>=0;i--)   //固定右边,找[0,i-1]中合适的位置{int left=0,right=i-1;while(left<right){if(nums[right]+nums[left]<=nums[i]) left++;else         //此时的nums[left],nums[right],nums[i]是满足条件的{           //区间[left,right-1]与nums[right],num[i]都可以组成三角型ret+=right-left;  right--;}}}return ret;}
};

1577. 数的平方等于两数乘积的方法数

题解:类似于两数求和,分别遍历nums1和nums2,在另一个数组中使用同向求和来找合适的位置。

class Solution {int ret;void twosum(vector<int>& nums,long long target){int n=nums.size();int left=0,right=n-1;while(left<right){if((long long)nums[left]*nums[right]>target) right--;else if((long long)nums[left]*nums[right]<target) left++;else {int l=1,tmp=left+1;while(tmp<right&&nums[tmp]==nums[tmp-1]) l++,tmp++;  //计算left有多少个相同的数ret+=l;right--;}}}public:int numTriplets(vector<int>& nums1, vector<int>& nums2) {//依旧是两数之和题型//遍历nums1,将nums[i]^2作为target在nums2中找满足的个数sort(nums1.begin(),nums1.end());sort(nums2.begin(),nums2.end());int n1=nums1.size(),n2=nums2.size();ret=0;for(int i=0;i<n1;i++)twosum(nums2,(long long)nums1[i]*nums1[i]);  //进行两数求和for(int i=0;i<n2;i++)twosum(nums1,(long long)nums2[i]*nums2[i]);return ret;}
};

923. 三数之和的多种可能

题解:此题时三数之和的变形题,依旧采用遍历数组,再通过相向双指针进行统计合适的个数。但是要注意的是此题时允许右重复组合出现的,所以可以先对数组各个元素的个数进行统计。

class Solution {
public:int threeSumMulti(vector<int>& nums, int target) {//三数求和变形题unordered_map<int,int> mm;for(auto e:nums) mm[e]++;  //记录每个数据的个数int n=nums.size();sort(nums.begin(),nums.end());int ret=0;for(int i=0;i<n;i++)  //遍历数组{//进行两数求和if(--mm[nums[i]]==0) mm.erase(mm[nums[i]]);int left=i+1,right=n-1;while(left<right){if(nums[left]+nums[right]+nums[i]>target) right--;else if(nums[left]+nums[right]+nums[i]<target) left++;else{//防止left与right的值相等造成的重复计数if(nums[left]==nums[right]) ret=(ret+(mm[nums[right]]-1)*mm[nums[right]]/2)%1000000007;  else ret=(ret+mm[nums[right]]*mm[nums[left]])%1000000007;right--,left++;while(left<right&&nums[left]==nums[left-1]) left++;   //取出重复元素while(left<right&&nums[right]==nums[right+1]) right--;   //取出重复元素}}}return ret;}
};

948. 令牌放置

题解:此题的关键在于如何将利益最大化,根据题意可以看出用已有得分获得最大能量,再用已有能量买最小令牌获取得分即可实现利益最大化。

class Solution {
public:int bagOfTokensScore(vector<int>& tokens, int power) {//当能量不够时,保证每次都使用1分都获取最大的能量//当能量充足的时候,使用能量换取最小的令牌来获得1分,这样就可以使得利益最大化int n=tokens.size();sort(tokens.begin(),tokens.end());  //先进行排序int left=0,right=n-1;int ret=0;while(left<right){if(power<tokens[left])  //能量不够,用得分获得最多能量{if(ret==0) return 0;ret--;power+=tokens[right--];}power-=tokens[left++]; //用最小能量获取得分ret++;}if(left==right&&power>=tokens[left]) ret++;  //判断相等位置是否还可以获得得分return ret;}
};

11. 盛最多水的容器

题解:经典的最大蓄水池问题,采用同向双指针通过每次循环确定一边的最大值即可。

class Solution {
public:int maxArea(vector<int>& height) {//通过左右两边双指针可以直接确定一个挡板可以存储的最多水量//eg:示例1,[0,n-1]此时的0比nums[n-1]小,所以选取0作为一个挡板可以存储的最多水量是[0,n-1]//此时0的最大储水量已经确定了,让0--->1继续向后找;此时nums[n-1]比nums[1]小,//所以以n-1作为一个挡板最大储水量是[1,n-1]此时n-1位置已经确定了,可以让n-1--->n-2//通过同向双指针就可以实现找到最大容量的效果int left=0,right=height.size()-1;int ret=0;while(left<right){if(height[left]>height[right]) {ret=max(ret,height[right]*(right-left));right--;}else {ret=max(ret,height[left]*(right-left));left++;}}return ret;}
};

42. 接雨水

解答一:同向双指针,与“盛最大水的容器”类似,此题依旧是让柱子矮的一边进行移动,但是此时每一次循环确定的是一个位置可以存储的水量,为了获取该位置的水量还需要分别存储左右两边的最长柱子。

class Solution {
public:int trap(vector<int>& height) {//此题可以使用双指针进行实现,依旧移动柱子矮的一边,但是在每一次只是将一列进行注水//并且需要存储左右两边最长的柱子int left=0,right=height.size()-1;int left_max=0,right_max=0;int ret=0;while(left<right){if(height[left]<height[right]){left_max=max(left_max,height[left]);ret+=left_max-height[left++];}else {right_max=max(right_max,height[right]);ret+=right_max-height[right--];}}return ret;}
};

解法二:使用前缀和进行实现;假设右边柱子无穷高,从左向右来确定每个位置可以存储的最大水量;再假设左边柱子无穷高,从右向左来确定每个位置可以存储的最大水量;再取每一个位置的最小值即可得出该位置的真实存水量。

class Solution {
public:int trap(vector<int>& height) {int n=height.size();vector<int> left_area(n);vector<int> right_area(n);int left_max=0,right_max=0;for(int i=0;i<n;i++)   //求从左向右的储水量{left_max=max(left_max,height[i]);left_area[i]=left_max-height[i];}for(int i=n-1;i>=0;i--)   //求从右向左的储水量{right_max=max(right_max,height[i]);right_area[i]=right_max-height[i];}int ret=0;for(int i=0;i<n;i++)  //取两个储水量的较小值即可ret+=min(right_area[i],left_area[i]);return ret;}
};

1616. 分割两个字符串得到回文串

题解:使用同向双指针找到左右两边第一个不对称的位置,以该位置作为基点判断能否形成回文字符串即可。

class Solution {
public:bool same(string& s)  //判断是否是回文字符串{int left=0,right=s.size()-1;while(left<right)if(s[left++]!=s[right--]) return false; return true;}bool checkP(string prev,string next){//通过同向双指针找第一个不相同的位置,以该位置作为基点将左右两边进行拼接,判断拼接后是否形成回文字符串int n=prev.size();int left=0,right=n-1;while(left<right){if(prev[left]!=next[right]) {//[0,left)和[left,n)string tmp1=prev.substr(0,left);tmp1+=next.substr(left,n-left);//[0,right]和(right,n)string tmp2=prev.substr(0,right+1);tmp2+=next.substr(right+1,n-right-1);return same(tmp1)||same(tmp2);}left++,right--;}return true;}bool checkPalindromeFormation(string a, string b) {   return checkP(a,b)||checkP(b,a);}
};

同向双指针

与相向双指针相反,两个指针都向左或者都向右。

611. 有效三角形的个数

题解:关于该题前面在实现的时候使用了:固定最大边+相向双指针进行实现,此处可以使用固定最小边+同向双指针进行实现。

class Solution {
public:int triangleNumber(vector<int>& nums) {//此题可以采用固定最小边a,求另外两条边b,c;需要满足c-b<a//所以b和c之间的距离不能太大,所以可以使用同向双指针int n=nums.size(),ret=0;sort(nums.begin(),nums.end());for(int i=0;i<n-2;i++){int a=nums[i];if(a==0) continue;   //对于长度为0的边不能参与计数//进行同向双指针int count=0,left=i+1;for(int right=i+2;right<n;right++){while(left<right&&nums[right]-nums[left]>=a) left++;count+=right-left;}ret+=count;}return ret;}
};

此处同向双指针与相向双指针类似就不过多举例了。

背向双指针

两个指针从数组中的同一个位置出发,一个向左,另一个向右,背向移动。

1793. 好子数组的最大分数

题解:从k位置使用两个指针分别向左右两边进行扩展;在扩展的时候每次向外扩1,所以要考虑是向左还是向右进行扩展,此处为了保证区间中的最小值尽可能的大,选取左右两边的较大值进行扩展。

class Solution {
public:int maximumScore(vector<int>& nums, int k) {//使用双指针让cur1和cur2分别从k位置向左,向右走//向左右两边扩展的时候尽量选择较大的值,来保证min的结果不会太小int n=nums.size();int cur1=k-1,cur2=k+1;int ret=nums[k],Min=nums[k];while(cur1>=0&&cur2<n){if(nums[cur1]>nums[cur2])  //当cur1的值大于cur2的时候向cur1那边扩{Min=min(Min,nums[cur1]);ret=max(ret,Min*(cur2-cur1));cur1--;}else    //当cur2的值大于cur1的时候向cur2那边扩{Min=min(Min,nums[cur2]);ret=max(ret,Min*(cur2-cur1));cur2++;}}while(cur1>=0)  //解决有一边还没有扩完的情况{Min=min(Min,nums[cur1]);ret=max(ret,Min*(cur2-cur1));cur1--;}while(cur2<n){Min=min(Min,nums[cur2]);ret=max(ret,Min*(cur2-cur1));cur2++;}return ret;}
};

原地修改

原地修改类题型:对数组进行修改使得修改后的数组满足题意;原地修改的方法不固定可能会使用相向双指针,同向双指针也可能会使用背向双指针。

27. 移除元素

题解:可以使用同向双指针来实现,使用prev,cur来记录位置,cur在前面走,当nums[cur]!=val时将cur的值赋给prev,否则让cur++即可。

class Solution {
public:int removeElement(vector<int>& nums, int val) {int n=nums.size();int prev=0,cur=0;while(cur<n){if(nums[cur]!=val)nums[prev++]=nums[cur];cur++;}return prev;}
};

26. 删除有序数组中的重复项

题解:使用同向双指针prev,cur解决,cur在前面走,当cur对应的位置满足条件就将cur的元素赋值给prev即可;使用map记录cur前已经出现过的数字。

class Solution {
public:int removeDuplicates(vector<int>& nums) {//使用双指针prev,cur,cur向前走当cur位置的元素满足条件时将cur的元素赋值给prev即可//使用map来记录cur前面已经存在的元素unordered_map<int ,int> mm;int prev=0,n=nums.size();for(int cur=0;cur<n;cur++){if(mm.count(nums[cur])==0) {mm[nums[cur]]++;nums[prev++]=nums[cur];}}return prev;}
};

80. 删除有序数组中的重复项 II

题解:与上一题一样,只需要将cur赋值给prev的条件进行修改即可。

class Solution {
public:int removeDuplicates(vector<int>& nums) {//使用双指针prev,cur,cur向前走当cur位置的元素满足条件时将cur的元素赋值给prev即可//使用map来记录cur前面已经存在的元素unordered_map<int ,int> mm;int prev=0,n=nums.size();for(int cur=0;cur<n;cur++){if(mm[nums[cur]]<2) {mm[nums[cur]]++;nums[prev++]=nums[cur];}}return prev;}
};

283. 移动零

题解:将数组从的0移动到后面----->将数组中的非零移动到前面即可。

class Solution {
public:void moveZeroes(vector<int>& nums) {//将0移动到末尾------->将非零移动到数组前面int prev=0,n=nums.size();for(int cur=0;cur<n;cur++){if(nums[cur]!=0) nums[prev++]=nums[cur];}//此时非0全部移动到前面了//将prev后面的元素全部置为0即可while(prev<n) nums[prev++]=0;}
};

905. 按奇偶排序数组

题解:将偶数放到数组前面,将奇数放到数组后面;可以使用相向双指针left,right;让left向右找奇数,让right向左找偶数,找到后将left和right进行交换即可。

class Solution {
public:vector<int> sortArrayByParity(vector<int>& nums) {//将偶数放在前面,将奇数放在后面//使用相向双指针进行实现,从让left向右走找奇数;让right向左走找偶数int left=0,right=nums.size()-1;while(left<right){while(left<right&&nums[left]%2==0) left++;  //找奇数 while(left<right&&nums[right]%2==1) right--;  //找偶数swap(nums[left],nums[right]);  //将奇偶进行交换}return nums;}
};

922. 按奇偶排序数组 II

题解:与上一题类似;根据题目可以分析出数组元素个数是偶数个,并且奇数位放奇数,偶数位放偶数即可。所以依旧是使用同向双指针left,right,但是left和right每次移动两步。

class Solution {
public:vector<int> sortArrayByParityII(vector<int>& nums) {//将偶数放在前面,将奇数放在后面//使用相向双指针进行实现,从让left向右走找奇数;让right向左走找偶数int n=nums.size();int left=0,right=n-1;while(left<n&&right>0){while(left<n&&nums[left]%2==0) left+=2;  //找奇数 while(right>0&&nums[right]%2==1) right-=2;  //找偶数if(left<n&&right>0)swap(nums[left],nums[right]);  //将奇偶进行交换}return nums;}
};

3467. 将数组按照奇偶性转化

题解:先遍历数组将数组中的奇数置为1,将数组中的偶数置为0;再将数组中的0放到数组前面即可。

class Solution {
public:vector<int> transformArray(vector<int>& nums) {//将数组中的奇数-->1,偶数---->0;再将数组中的0放到数组前面for(auto& e:nums) e=e%2==0?0:1;int prev=0,n=nums.size();for(int cur=0;cur<n;cur++)if(nums[cur]==0) nums[prev++]=nums[cur];while(prev<n) nums[prev++]=1;return nums;}
};  

2460. 对数组执行操作

题解:模拟+同向双指针;

class Solution {
public:vector<int> applyOperations(vector<int>& nums) {int n=nums.size();for(int i=0;i<n-1;i++){if(nums[i]==nums[i+1]){nums[i]*=2;nums[i+1]=0;}}//将非0放到数组前面int prev=0;for(int cur=0;cur<n;cur++){if(nums[cur]!=0) nums[prev++]=nums[cur];}while(prev<n) nums[prev++]=0;return nums;}
};

1089. 复写零

题解:模拟+同向双指针;先模拟规则找到最后一个移动的元素,再从后往前进行复写。

class Solution {
public:void duplicateZeros(vector<int>& nums) {//先通过模拟找到返回数组中的最后一位int n=nums.size();int prev=0,cur=0;while(cur<n){if(nums[prev]==0) cur++;cur++,prev++;}//此时prev-1就是输出数组的最后一位//从prev开始向前移动prev-=1;//注意:有一种特殊情况,当最后一位是0的时候进行向后移只剩一个数没有覆盖,所以只有一个0,没有复写if(cur==n+1) nums[n-1]=0,cur=n-2,prev-=1;  //进行特殊处理,将末尾没有复写的0直接加到结尾即可else cur=n-1;for(;prev>=0;prev--){nums[cur--]=nums[prev];if(nums[prev]==0) nums[cur--]=0; //进行复写}}
};

总结

关于双指针的题目关键在于如何理解题目,两个指针在何时才能向前或向后移动;滑动窗口其实也是双指针的以内,其也属于同向双指针。题目讲解到此就结束了,感谢阅读,让我们在下一篇算法中再见。。

相关文章:

  • 原生小程序+springboot+vue+协同过滤算法的音乐推荐系统(源码+论文+讲解+安装+部署+调试)
  • 文件上传Ⅲ
  • css:倒影倾斜效果
  • OpenAI与微软洽谈新融资及IPO,Instagram因TikTok流失四成用户
  • TRTC实时对话式AI解决方案,助力人机语音交互极致体验
  • 东方通2024年报分析:信创国产化龙头的蓬勃发展与未来可期
  • linux,我启动一个springboot项目, 用java -jar xxx.jar ,但是没多久这个java进程就会自动关掉
  • Python web 开发 Flask HTTP 服务
  • 在 Ubuntu 系统中,将 JAR 包安装为服务
  • 影楼精修-肤色统一算法解析
  • 【计算机网络】HTTP/1.0,HTTP/1.1,HTTP/2,HTTP/3汇总讲解,清晰表格整理面试重点对比
  • Python Socket编程:实现简单的客户端-服务器通信
  • 微服务初步学习
  • 使用seatunnel同步磐维数据库数据
  • 文章记单词 | 第82篇(六级)
  • 掌握 LangChain 文档处理核心:Document Loaders 与 Text Splitters 全解析
  • uniapp婚纱预约小程序
  • MySQL 8.0 OCP 1Z0-908 131-140题
  • neo4j框架:java安装教程
  • 云轴科技ZStack官网上线Support AI,智能助手助力高效技术支持
  • 高途一季度净利润同比增长1108%: “与吴彦祖一起学英语”短时间内就实现了盈利
  • 北京警方:海淀发生小客车刮碰行人事故4人受伤,肇事司机已被查获
  • 美F-35险被胡塞武装击中,损失增大让行动成“烂尾仗”
  • 湃书单|澎湃新闻编辑们在读的14本书:后工作时代
  • 《求是》杂志发表习近平总书记重要文章《锲而不舍落实中央八项规定精神,以优良党风引领社风民风》
  • 工商银行杭州金融研修院原院长蒋伟被“双开”