[TOC]

一、双指针的LeetCode题目

1、删除数组中的元素

(1)删除数组中的某个(所有的)目标值

27. 移除元素(简单难度)

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int n=nums.size();
        // 数组中至少有0个元素
        int slow=0; // 慢指针,[0,low),指向删除后的区间的最后一个元素的后一个索引
        int fast = 0;// 快指针,负责遍历整个数组
        while(fast < n)
        {
            //快指针不等于目标值时
            if (nums[fast] != val) 
            {
                nums[slow] = nums[fast];//快指针指向的数覆盖给慢指针指向的数
                slow++;//慢指针+1
                fast++; //快指针+1
            }
            // 快指针等于目标值时,快指针+1
            else{
                fast++;//快指针+1
            }
        }
        return slow;//返回慢指针
    }
};

(2)删除有序数组中的重复项

26. 删除有序数组中的重复项(简单难度)

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n=nums.size();
        if(n==0) return 0;
        // 数组中至少有1个元素
        int slow=1;// 慢指针,[0,low),指向删除后的区间的最后一个元素的后一个索引
        int fast=1;// 快指针,负责遍历整个数组
        while(fast<n){
            // 快指针和上一个元素不同时,
            if(nums[fast]!=nums[slow-1]){
                nums[slow]=nums[fast];// 快指针元素覆盖慢指针元素
                slow++;//慢指针前进
                fast++;//快指针前进
            }
            // 快指针和上一个元素相同时,快指针前进
            else{
                fast++;
            }
        }
        return slow;
    }
};

80. 删除有序数组中的重复项 II(中等难度)

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n=nums.size();
        if(n<=2) return n;
        // 数组中至少有2个元素
        int slow=2;// 慢指针,[0,low),指向删除后的区间的最后一个元素的后一个索引
        int fast=2;// 快指针,负责遍历整个数组
        while(fast<n){
            // 快指针不等于
            if(nums[fast]!=nums[slow-2]){
                nums[slow]=nums[fast];
                slow++;
                fast++;
            }
            else{
                fast++;
            }
        }
        return slow;
    }
};

2、移动数组中的元素

283. 移动零(简单难度)

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int n=nums.size();
        // 数组中至少有0个元素
        int slow=0;// 慢指针,[0,low),指向移动后的区间的最后一个元素的后一个索引
        int fast=0;// 快指针,负责遍历整个数组
        while(fast<n){
            // 如果快指针不为0
            if(nums[fast]!=0){
                swap(nums[slow],nums[fast]);// 快慢交换
                slow++;
                fast++;
            }
            else{
                fast++;
            }
        }
    }
};

75. 颜色分类(中等难度)

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int n=nums.size();
        if(n<2) return;
        // 整个数组需要分成3个区间:
        int zero = 0;//[0,zero),红色元素,存放0
        int one = 0;//[zero,one),白色元素,存放1,同时负责遍历
        int two = n-1;// [one,two),篮色元素,存放2
        while (one < n) {
            // 如果当前元素==0,移动到头部
            if (nums[one] == 0&&zero<one) {
                swap(nums[one], nums[zero]);//
                zero++;
            }
            // 如果当前元素==2,移动到表尾
            else if (nums[one] == 2&& one<two) {
                swap(nums[one], nums[two]);
                two--;
            }
            // 如果当前元素==1,跳过
            else {
                one++;
            }
        }
        //quickSort(nums,0,nums.size()-1);
    }
}

215. 数组中的第K个最大元素(中等难度)

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        int len = nums.size();
        return quickFind(nums,k-1,0,len-1);//从大到小排序,第k大就是第k-1个索引,返回第k-1个索引的值
    }
    // 快排法
    int quickFind(vector<int>& nums, int k,int left,int right){
        int index = partition(nums,left,right);//开始分区
        // 结束分区
        if(index==k){
            return nums[index];//first是基准值索引,表示第first个
        }
        else if(index>k){
            return quickFind(nums, k,left,index-1);
        }
        else {
            return quickFind(nums, k,index+1,right);
        }
    }
    // 分区函数
    int partition (vector<int> &nums, int first, int last) {
        int key = nums[first];//默认第一个为基准值
        while (first < last) {
            // 从右往左找到第一个小于key的值
            while (first < last && nums[last] <= key) --last;
            nums[first] = nums[last];
            // 从左往右找到第一个大于key的值
            while (first < last && nums[first] >= key) ++first;
            nums[last] = nums[first];
        }
        nums[first] = key;// 基准值归位
        return first;// 返回索引
    } 
};

905. 按奇偶排序数组(简单难度)

class Solution {
public:
    /*思路:这道题解决不难,使用额外的数组,只要会判断奇偶数就可以分出两类数,难点在于原地排序算法:双指针的方法:
    左指针寻找奇数值,右指针寻找偶数值,当符合交换条件时,进行两数交换;
    否则指针继续左右运动,寻找符合条件的奇偶值。
    当两指针相遇时,结束循环。*/
    vector<int> sortArrayByParity(vector<int>& nums) {
        int left=0;//左指针
        int right=nums.size()-1;//右指针
        // 遍历数组[left,right],
        while(left<right){
            // 如果左指针指向奇数,右指针指向偶数,就交换两个数
            if (nums[left] % 2 > nums[right] % 2) {
                swap(nums[left],nums[right]);
            }
            // 如果左指针指向偶数,左指针移动
            if (nums[left] % 2 == 0) left++;
            // 如果右指针指向奇数,右指针移动
            if (nums[right] % 2 == 1) right--;
        }
        return nums;
    }
};

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面(简单难度)

class Solution {
public:
    /*思路:这道题解决不难,使用额外的数组,只要会判断奇偶数就可以分出两类数,难点在于原地排序算法:双指针的方法:
    左指针寻找偶数值,右指针寻找奇数值,当符合交换条件时,进行两数交换;
    否则指针继续左右运动,寻找符合条件的奇偶值。
    当两指针相遇时,结束循环。*/
    vector<int> exchange(vector<int>& nums) {
        int left=0;//左指针
        int right=nums.size()-1;//右指针
        // 遍历数组[left,right],
        while(left<right){
            // 如果左指针指向偶数,右指针指向奇数,就交换两个数
            if (nums[left] % 2 < nums[right] % 2) {
                swap(nums[left],nums[right]);
            }
            // 如果左指针指向奇数,左指针移动
            if (nums[left] % 2 == 1) left++;
            // 如果右指针指向偶数,右指针移动
            if (nums[right] % 2 == 0) right--;
        }
        return nums;
    }
};

922. 按奇偶排序数组 II(简单难度)

class Solution {
public:
    /*思路:这道题解决不难,使用额外的数组,只要会判断奇偶数就可以分出两类数,难点在于原地排序算法:双指针的方法:
    遍历所有偶数索引*/
    vector<int> sortArrayByParityII(vector<int>& nums) {
        //
        int n = nums.size();
        int j = 1;//奇数索引
        // 遍历整个数组的所有偶数索引
        for (int i = 0; i < n; i += 2) {
            // 如果偶数索引上的数是奇数
            if (nums[i] % 2 == 1) {
                // 连续遍历整个数组的所有奇数索引,找到一个数是偶数
                while (nums[j] % 2 == 1) j += 2;
                swap(nums[i], nums[j]);//交换两个数
            }
        }   
        return nums;
    }
};

3、查找排序数组中的2个数

剑指 Offer 57. 和为s的两个数字(简单难度)

剑指 Offer II 006. 排序数组中两个数字之和(简单难度)

class Solution {
public:
    /*解析:这道题在一个排序数组中查找2个数,使其和为target
    提到排序,可能会选用二分查找法,二分查找法确实可以结合使用
    但是这道题难度简单,用普通的双指针就可以搞定
    排序数组的特点可以作为移动左右指针的依据,和较小右移左指针,和较小左移右指针
    */
    vector<int> twoSum(vector<int>& nums, int target) {
        int left=0;//左指针
        int right=nums.size()-1;//右指针
        int sum=0;
        while(left<=right){
            sum=nums[left]+nums[right];
            // 如果两数和==target,直接返回
            if(sum==target) return {nums[left],nums[right]};
            // 如果两数和>target,说明应该降低两数和,右指针左移即可
            else if(sum>target) right--;
            // 如果两数和<target,说明应该增加两数和,左指针右移即可
            else left++;
        }
        return {nums[left],nums[right]};
    }
};

二、滑动窗口的LeetCode题目

1、连续递增子数组

674. 最长连续递增序列(简单难度)

子数组是原数组中连续的元素,中间不可以删除或添加其他元素,每个元素的相对顺序和原数组相同

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        int len=nums.size();
        if(len==1) return 1;
        int res=0;// 最长长度
        int left=0;
        int right=1;
        // 滑动窗口[left,right)表示递增区间,负责搜索数组中的所有递增区间
        while(right<len){
            // 如果新元素递增,扩展右边界
            if(nums[right-1]<nums[right]){
                right++;
            }
            // 如果新元素非递增,更新左边界,扩展右边界
            else{
                left=right;
                right++;
            }
            res=max(res,right-left);//不断更新递增区间的最大长度
        }
        return res;
    }
};

剑指 Offer 57 - II. 和为s的连续正数序列(简单难度)

class Solution {
public:
    /*解析:本题是从正整数序列中查找所有和为target的连续子序列
    难点在于每个连续子序列的长度、数量是不确定的。
    这种类型题,特别是元素都是正整数,特别适合滑动窗口算法。
    请注意,左指针和右指针的移动时机
    当区间和>=target,右移左指针;当区间和<target,右移右指针
    进阶:求解区间和的数学技巧:(left+right)*(right-left+1)/2
    */
    vector<vector<int>> findContinuousSequence(int target) {
        vector<vector<int>> res;
        int left=1;//正整数序列的第1个数
        int right=2;//正整数序列的第2个数
        int sum=0;
        vector<int> sub;
        // 滑动窗口[left,right]
        while(left<right){
            // 计算滑动窗口[left,right]的和
            sum=0;
            sub.clear();
            for(int i=left;i<=right;i++){
                sum+=i;
                sub.push_back(i);
            }
            // 如果区间和==target,移动左指针
            if(sum==target) {
                res.push_back(sub);
                left++;
            }
            // 如果区间和<target,右指针右移
            else if(sum<target) right++;
            // 如果区间和>target,左指针右移
            else left++;
        }
        return res;
    }
};

添加数学公式优化后:

class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        vector<vector<int>> res;
        int left=1;//正整数序列的第1个数
        int right=2;//正整数序列的第2个数
        int sum=0;
        vector<int> sub;
        // 滑动窗口[left,right]
        while(left<right){
            // 计算滑动窗口[left,right]的和
            sum=(left+right)*(right-left+1)/2;
            // 如果区间和==target,移动左指针
            if(sum==target) {
                sub.clear();
                for(int i=left;i<=right;i++){
                    sub.push_back(i);
                }
                res.push_back(sub);
                left++;
            }
            // 如果区间和<target,右指针右移
            else if(sum<target) right++;
            // 如果区间和>target,左指针右移
            else left++;
        }
        return res;
    }
};