双指针滑动窗口刷题笔记C++版本

双指针刷题笔记


本文是力扣刷题笔记总结,题目地址见https://leetcode-cn.com/leetbook/read/array-and-string

内容为最后两章

本文内容包含:

1、双指针的思想

2、滑动窗口

3、实例一 移动数组中的零

4、实例二 移除数组中的重复元素

5、实例三 反转字符串中的单词

6、实例四 反转字符串

7、实例五 数组拆分

8、实例六 两数之和(有序数组)

9、实例七 移除元素

10、实例八 最大连续1的个数

11、实例九 长度最小的子数组

双指针的思想

双指针即使用两个指针来遍历数组。以翻转数组内容为例,指针i指向数组头部,指针j指向数组尾部,交换指针i与指针j指向的数组内容,之后,i向后走,j先前走,即如下代码所示:

 vector<int> nums[...];
int i = 0;
int j = nums.length()-1;
while(i<j){
    swap(nums[i],nums[j]);
    ++i;
    --j;
}

这种技巧适用于从两端向中间迭代的问题模型。

滑动窗口

滑动窗口是双指针的一种。在滑动窗口中,同样有两个指针,一个称为慢指针,记为i,一个称为快指针,称为j,由于i和j的移动速度不同,(j-i)的值也在不断变化,就像一个不断变化长度的窗口,因此称为滑动窗口。

以此问题为例:

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

(图摘自力扣)

//没有真正的移除元素,只是返回了移除重复元素后的数组长度
//只有当j指向的值不是val的时候,i才向后移动
int i=0;
int j=0;
while(j<nums.length()-1){
    if(nums[j]!=val){
        nums[i]=nums[j];//保存所有不等于val的数组项
        ++i;
    }
    ++j; 
}
return i;

在这里插入图片描述

移除数组中的零

题目描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

//使用滑动窗口,i之前的为已经处理好的串,j以及j之后的串为尚未处理的串
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int i=0;//慢指针
        int j=0;//快指针
        while(j<nums.size()){
            if(nums[j]!=0){
                swap(nums[i],nums[j]);
                ++i//i指针后移
            }//依次保存所有的非零元素
            ++j;//j指针后移
        }
        return i;//返回元素个数,数组下标从0开始,故返回i(nums[i]=0)
    }
};

实际上,在j指向的数组内容不为0的时候,i随j同步向后走,也即是i与j指向同一个元素。在j遇到0的时候,i不动(实际此时i指向的是0),j一直向下走到下一个非0的位置,将此位置的值与i指向的0交换

假如此时数组为1,2,0,0,3,将会是如下情况:
1、i=0 j=0 nums[j]!=0 交换nums[0]与nums[0] nums[0]=1 i=1 j=1
2、nums[j]=nums[1]!=0 交换nums[1]与nums[1] nums[1]=2 i=2 j=2
可以看到,在j第一次遇到0之前,每执行一次循环体,i与j都同步后移
3、num[j=2] ==0 j=3
4、nums[j=3] == 0 j=4
5、nums[4]!=0 交换nums[i=2]与nums[j=4]
6、函数返回

可以看出来,每一次i停止前进的时候,nums[i]均为0,也即是前i-1个元素都是符合题目要求的

移除数组中的重复元素

题目描述:给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

注意是重复出现,也就是不会有这种情况:[1,1,2,1]

此题与上题的思路相同,只是条件有所变化,同样假设i以及之前的串已经处理完毕,j以及j之后的串待处理

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int i=0;
        int j=0;
        if(nums.size()==0)
            return 0;
        while(j<nums.size()){
            if(nums[i]!=nums[j]){
                swap(nums[++i],nums[j]);
            }
            right++;
        }
        return left+1;
    }
};

反转字符串中的单词

题目描述:给定一个字符串,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

对每一个单词进行翻转即可。

class Solution {
public:
    string reverseWords(string s) {
        int leng=s.length();
        int i=0;
        int j=0;
        while(j!=leng){
            if(s[j]==' '||j==leng-1){//如果j走到空格或者末尾,此时i、j之间的串为一个单词,对此单词进行翻转
                if(j==leng-1)
                    reverse(s.begin()+i,s.begin()+j+1);//最后一个单词特殊处理
                else
                    reverse(s.begin()+i,s.begin()+j);
                ++j;
                i=j;//i移向下一个单词的开头
            }
            else{
                j++;//向后寻找空格
            }
        }
        return s;
    }
};

反转字符串

题目描述:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符

class Solution {
public:
    void reverseString(vector<char>& s) {
        char temp;
        int len=s.size();
        for(int i=0;i<len/2;i++){
            temp=s[i];
            s[i]=s[len-i-1];
            s[len-i-1]=temp;
        }
    }
};

数组拆分

题目描述:

给定长度为 2n 的整数数组 nums ,你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), …, (an, bn) ,使得从 1 到 n 的 min(ai, bi) 总和最大。

分析:要使得min(ai,bi)最大,需要尽可能的囊括数组中的大值,假设数组为[1,2,3,4,5,6]

对于6,最大,可以使用6来囊括5,即(5,6)

对于4,为剩下的数据中最大的,可以用来囊括3,即(4,3)

剩下(2,1)

最大和为5+3+1=9

可以发现,得到的是偶数下标数据之和

class Solution {
public:
    int arrayPairSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        int res=0;
        for(int i=0;i<nums.size();++i){
            res=res+nums[i];
            ++i;、、偶数下标数据之和
        }
        return res;
    }
};

两数之和(有序数组)

题目描述:给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。

函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。

使用双指针。i从头开始,j从尾开始,相加之和小于target,由于数组升序排列,i++,否则,j++,直至相等。

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int len=numbers.size();
        int i=0;
        int j=len-1;
        while(numbers[i]+numbers[j]!=target){
            if(numbers[i]+numbers[j]<target)
                i++;
            else
                j--;
        }
        return {i+1,j+1};
    }
};

移除元素

题目描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

分析见滑动窗口

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

最大连续1的个数

题目描述:给定一个二进制数组, 计算其中最大连续 1 的个数。

使用双指针。思路为:在j未指向1时,i与j同步前进。当j指向1时,i不再前进,j前进,j每前进一步,如果未指向0,便计算此时子串长度,更新结果,如果指向了0,i、j继续同步前进,寻找下一个1串

也即是i始终指向1串首部。

class Solution {
public:
    int findMaxConsecutiveOnes(vector<int>& nums) {
        int i=0;
        int j=0;
        int maxsize=0;
        for(j=0;j<nums.size();j++){
            if(nums[j]==1){
                maxsize=(maxsize<j-i+1)?j-i+1:maxsize;
            }
            else{
                i=j+1;
            }
        }
        return maxsize;
    }
};

长度最小的子数组

题目描述:给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

使用滑动窗口。首先处理特殊情况,之后,i、j从0、1开始遍历。

1、当和小于target的时候,i不动,j向后走,直到子数组和大于等于target,此时计算该子数组的长度,对最小长度进行更新

2、j不懂,i向前走,直到子数组和小于target,回到1

不断重复直到j走到数组末尾

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int leng = nums.size();
        vector<int>::iterator it0=nums.begin();
        int i = 0;
        if (nums[i] >= target)
            return 1;//首元素大于target,结果为1
        int j = 1;
        int minLength = leng;//初始值
        int res;
        bool flag=false;
        while (j <= leng - 1) {
            res = accumulate(it0 + i, it0 +1+ j, 0);
            if (res >= target) {
                flag=true;//触发flag说明有解
                minLength = (minLength < (j - i + 1)) ? minLength : (j - i + 1);
                if(res-nums[i]>=target)
                    i++;
                else
                    j++;
            }
            else {
                j++;
            }
        }
        if(flag==0)
            return 0;//未触发flag,无解
        return minLength;
    }
};
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值