Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)

目录

快慢指针

滑动窗口

76. 最小覆盖子串

567. 字符串的排列

438. 找到字符串中所有字母异位词 

3. 无重复字符的最长子串

1248. 统计「优美子数组」

209. 长度最小的子数组

30. 串联所有单词的子串

239. 滑动窗口最大值

面试题57 - II. 和为s的连续正数序列

双指针

1. 两数之和 

15. 三数之和

16. 最接近的三数之和 

11. 盛最多水的容器

面试题 16.06. 最小差 

240. 搜索二维矩阵 II

面试题21. 调整数组顺序使奇数位于偶数前面

1471. 数组中的 k 个最强值


快慢指针

之前在链表总结部分,提及到了快慢指针,凡是牵扯到链表中的精准定位问题,选择使用快慢指针,保准没错。

环的个数/起点,中间位置,两条链表找相交处,看了题目描述,直接快慢指针,手到擒来。

LeetCode 链表总结:https://blog.csdn.net/qq_41605114/article/details/105385252

关于链表中的快慢指针使用,此处不再多嘴,详情见上方链接

好文分享:

https://leetcode-cn.com/problems/find-the-duplicate-number/solution/qian-duan-ling-hun-hua-shi-tu-jie-kuai-man-zhi-z-3/

 

滑动窗口

字符串或者数字之间求交集(在字符串中是子串),或者符合要求的连续子集或者子字符串,都可以使用滑动窗口进行解答。

 

滑动窗口的核心,就是在右侧区间不断扩大的同时,根据完成解的要求,右移左侧指针,依次找到最优解。

 

@powcai对滑动窗口的题目进行了汇总:

 https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/hua-dong-chuang-kou-by-powcai/

@labuladong关于滑动窗口,总结了一套模板:https://leetcode-cn.com/problems/minimum-window-substring/solution/hua-dong-chuang-kou-suan-fa-tong-yong-si-xiang-by-/%E5%8F%8C%E6%8C%87%E9%92%88%E6%8A%80%E5%B7%A7.md

模板如下:

/* 滑动窗口算法框架 */
void slidingWindow(string s, string t) {

    //(1)
    unordered_map need, window;
    for (char c : t) need[c]++;
    //(2)
    int left = 0, right = 0;
    int valid = 0; 
    while (right < s.size()) {
        // c 是将移入窗口的字符
        char c = s[right];
        // 右移窗口
        right++;
        // 进行窗口内数据的一系列更新
        ...

        /*** debug 输出的位置 ***/
        printf("window: [%d, %d)\n", left, right);
        /********************/
        
        // 判断左侧窗口是否要收缩
        while (window needs shrink) {
            // d 是将移出窗口的字符
            char d = s[left];
            // 左移窗口
            left++;
            // 进行窗口内数据的一系列更新
            ...
        }
    }
}

作者:labuladong
链接:https://leetcode-cn.com/problems/minimum-window-substring/solution/hua-dong-chuang-kou-suan-fa-tong-yong-si-xiang-by-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

以下内容也主要是参考上面提到的多篇文章并加入自己的理解。

 

模板介绍:

(1)中,初始化两个Hash表,window代表我们的滑动窗口,needs代表我们需要找的目标值及个数

(2)中,初始化滑动窗口的左右指针,整个过程中滑动窗口都是一个左闭右开的区间,即[left,right),其中valid表示窗口中满足needs条件的字符个数。

此模板是左闭右开,务必注意right和left更新的位置,更新的位置不一样,那么我们得到的解也就不一样


 

滑动窗口的核心,就是先找到一个满足要求的解,然后不断的收缩空间,尝试得到最优解。

right不断右移,扩大窗口范围,在扩大的过程中,不断判断,是否得到了一个满足要求的解,一旦得到一个解,在更新解的同时

开始进行窗口left的右移,我们开始缩小窗口,直到在窗口中失去解,完成lef的移动,right继续右移

我们也可以这么认为:

不断找到解,不断失去解,再次找到解。

首先找到一个解,然后移动left指针,直到区间失去了解,失去解是为了在剩下的内容中找到另一组解

失去解后,right不断右移,直到我们再次找到解。以此循环,知道遍历所有要查找内容。

如果说二分查找的精华在区间中实在有解,滑动窗口的精髓在于舍弃解后,再次寻找解。

 

下面找一道题目作为依托,进一步阐述滑动窗口的原理

76. 最小覆盖子串

https://leetcode-cn.com/problems/minimum-window-substring/

class Solution {
public:
    string minWindow(string s, string t) {
        unordered_map window,need;
        for(auto item:t) need[item]++;//目标元素出现次数
        int left = 0,right = 0,valid = 0;
        int length = INT_MAX,begin = 0;
        int size = need.size();
        while(right < s.size())
        {
            char Rtemp = s[right];//当前字符
            right++;
            if(need[Rtemp])//一旦当前字符是目标值之一,那么我们进行记录,目前窗口包含目标值
            {
                window[Rtemp]++;//更新窗口包含目标值的情况
                if(window[Rtemp] == need[Rtemp])
                //一旦某个目标值元素在窗口中的个数满足要求,那么更新valid
                valid++;
            }

            while( valid == size )//一旦条件成立,说明窗口中目前已经包含了解
            {
                //更新解
                if(right - left

下面我们来图解一下整个过程:

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第1张图片  

right不断地右移,知道指向C,此时valid == 3,right照常自增1。第一次找到解,此时更新解,len = 6,begin = 0;

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第2张图片

之后left右移,从解中删除A,left自动增1,valid == 2,区间内不包含解,那么直接break,right继续右移

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第3张图片

 

知道right找到最后的A,right自增1,valid == 3,找到第二个解,但是不如第一个解短,不更新解,left开始右移

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第4张图片

直到将C移除区间,right继续右移

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第5张图片

当right在等于s.size()的时候,选择C,然后自增1,此时valid等于3,left开始右移

当left选中E的时候,left自增,到达了B的位置,此时进入新的一轮循环,更新解,排除B后,valid也不在等于3,跳出循环

此时right也不符合条件,跳出循环。

最优解已经被记录,完成。

最重要的细节部分,是对区间和最优解的更新位置,务必注意,因为左闭右开,right在更新解前自增,left在更新解后自增

567. 字符串的排列

 https://leetcode-cn.com/problems/permutation-in-string/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第6张图片

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        unordered_map window,need;
        for(auto item : s1) need[item]++;
        int left = 0, right = 0;
        int len = INT_MAX;
        int valid = 0;
        int nsize = need.size(); //避免重复元素
        while(right

架构完全一样,改都没有改,本题只需要增加一些判断即可,需要是子串,而且要求无视排列。

那么只要在s2中找到一个子串,均包含s1,且长度和s1相等,那一定就是找到了,否则就是没有找到。

438. 找到字符串中所有字母异位词 

https://leetcode-cn.com/problems/find-all-anagrams-in-a-string/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第7张图片

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第8张图片Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第9张图片

class Solution {
public:
    vector findAnagrams(string s, string p) {
        unordered_map window,need;
        for(auto item:p) need[item]++;
        int right = 0,left = 0;
        int valid = 0,len = p.size();
        int nsize = need.size();//避免重复元素
        vector Res;
        while(right

此题和上一道题非常相似,都是子串,那么我们还是从长度入手进行解题。

 

但是有时我们需要的题目不一定如此复杂,下面的第3题和209题,就是滑动窗口的另外两种风格。

但是核心不变,就像二分查找的区间,不管左右区间如何迭代,解都是要在区间内的

滑动窗口也是如此,解一定要保持在窗口内,上面几道字符串类型的题目,都体现了滑动窗口的核心:

不断找到解,不断失去解,再次找到解。

首先找到一个解,然后移动left指针,直到区间失去了解,失去解是为了在剩下的内容中找到另一组解

失去解后,right不断右移,直到我们再次找到解。以此循环,知道遍历所有要查找内容。

如果说二分查找的精华在区间中实在有解,滑动窗口的精髓在于舍弃解后,再次寻找解。

下面的题目更是体现了这点。

3. 无重复字符的最长子串

https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第10张图片Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第11张图片

还是左右指针,滑动窗口的套路,

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        unordered_mapwindow;
        int right = 0,left = 0;
        int len = 0;
        while(right1)//清空部分
            {
                char ltemp = s[left];
                left++;
                window[ltemp]--;

            }
            len = max(len,right - left);
        }
        return len;
        
    }
};

window[temp]大于1的时候,说明有重复的了,那么我们把重复的排除出去即可,不断移动left指针,直到区间内再次恢复为只含

有单独元素的情况

 

滑动窗口的变形:

1248. 统计「优美子数组」

https://leetcode-cn.com/problems/count-number-of-nice-subarrays/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第12张图片Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第13张图片

本题在某种意义上,甚至是有些违背滑动窗口的一般结题思路的

滑动窗口一般是找到解,然后将解抛出,继续寻找最优解。

此题我们在找到解的时候,需要特别注意,如果理解抛出解,会少算很多情况

比如示例3

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第14张图片

以第一个2开头,就有四种情况,以第二个2开头也有四种,总共16种,所以我们的滑动窗口需要进行一下修改

本题我们需要在找到一组解后,计算这个解中两端奇数前后的偶数个数,算出排列组合的情况

class Solution {
public:
    int numberOfSubarrays(vector& nums, int k) {
        //滑动窗口
        int size = nums.size();
        if(size target;
        int leftnumber = 0,rightnumber = 0;
        while(right

使用两个int变量,leftnumber 和 rightnumber,分别记录第一个奇数距离left的距离,和最后一个奇数距离right的距离

找到排列组合之后,我们要进行滑动窗口的老操作了,就是边界收缩,让left不断收缩,直到没有解

这是一道非常非常有趣的滑动窗口题目。反常规。

 

 

209. 长度最小的子数组

https://leetcode-cn.com/problems/minimum-size-subarray-sum/

 Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第15张图片

class Solution {
public:
    int minSubArrayLen(int s, vector& nums) {
        int right = 0,left = 0;
        int sum = 0,len = INT_MAX;
        while(right=s) 
            {
                len = min(len,right - left);
                sum -= nums[left];
                left++;
            }
        }
        return len == INT_MAX?0:len;
    }
};

当和到达要求的时候,此时可以求长度了,我们得到了解,下面就要舍弃解,不断移动left,直到失去解

然后我们才能去找更优的解。

30. 串联所有单词的子串

https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/

参考内容:

https://leetcode-cn.com/problems/substring-with-concatenation-of-all-words/solution/30-by-ikaruga/

本题看似非常简单,不就是把找元素换找单词吗?步长从1改成单词长度,不就分分钟的事情吗?

但是此题的情况远比这复杂,因为题目可没有说,其他干扰项的长度也是步长

比如:

​
"lingmindraboofooowingdingbarrwingmonkeypoundcake"
["fooo","barr","wing","ding","wing"]


"aaaaaa"
["aaa","aaa"]

​

 看了简直要死,第一个例子,第一个大的干扰项长度是13,但是平常的步长是4

那么本题该如何应对,我们在滑动窗口外侧加循环,让整个滑动窗口的起点,从0开,一直到步长4为止,起点分别是

0,1,2,3,按照一个步长的循环来进行,这样总能规避掉各种长度的干扰项目。

题目要求找到的解,必须长度和words所有元素的长度加起来一致,但是排列无所谓,所以求子串是否是我们想要的解,判断条件就是长度相等

 

本题细节非常多,可谓是滑动窗口之最了

先看核心代码

            int left = i;
            int right = i,valid = 0;
            unordered_map window;
            while(right

①处,因为题目要求,不能含有其他多余字符,所以整个长度必须是匹配的

②处,这个地方写快了很容易出错,一旦少一个字符,我们就要将valid减去1

因为字典中每个字符都要出现,我们还是用Hash表进行记录,但是这样有时候会记录重复的内容

所以我们在加完temp后,立即比较,如果数量够了就将valid加1,之后有重复的也不管了

 

class Solution {
public:
    vector findSubstring(string s, vector& words) {
        int s_size = s.size(),w_size = words.size();
        if(s_size == 0||w_size == 0) return {};
        unordered_map need;
        for(auto item:words) need[item]++;//统计每个类型字符串出现的频率
        int validsize = need.size();
        int step = words[0].size();
        int length = w_size*step;//为了后面去除错误答案打下基础
        vector Res;
        for(int i = 0;i window;
            while(right

 

 

最后一题,借滑动窗口之名,而无滑动窗口之实。

239. 滑动窗口最大值

https://leetcode-cn.com/problems/sliding-window-maximum/ 

面试题59 - I. 滑动窗口的最大值  

https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第16张图片Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第17张图片

 

要求线性时间:我们可以这么做,维护一个栈,每次都选择滑动窗口中的最大值压入,之后每次窗口移动一次,只把串口right处的值和栈顶元素比较即可,线性时间复杂度,需要注意的是这个最值,一定要在滑动窗口的范围内:我们尝试实现一下代码:

我们先处理第一个滑动窗口:

        //先处理第一个滑动窗口
        int MAXnum = 0,index = 0;
        for(int i = 0;i

比较了k次,选出最值,那么我们下面从第二个滑动窗口开始:

int left = 1,right = k;//我们从第二个滑动窗口开始
        while(rightright) 
                {
                    MAXIndex.pop();
                    MAXnum = nums[left],index = left;
                    //只比较left到right-1这个区间内的值,下面还有一个if比较right的值
                    for(int i = left + 1;iMAXnum) {MAXnum = nums[i],index = i;}
                    } 
                    MAXIndex.push(index);
                }
                //一般情况下:只比较栈中的最值和right指针的位置
                if(nums[right]>nums[MAXIndex.top()]) {MAXIndex.pop();MAXIndex.push(right);}
            }
            else MAXIndex.push(right);
            //选择最值并添加到结果中,整个滑动窗口右移
            cout<

 完成代码如下:

class Solution {
public:
    vector maxSlidingWindow(vector& nums, int k) {
        if(nums.empty()) return {};
        vectorRes;
        stackMAXIndex;
        int size = nums.size();
        //先处理第一个滑动窗口
        int MAXnum = 0,index = 0;
        for(int i = 0;iright) 
                {
                    MAXIndex.pop();
                    MAXnum = nums[left],index = left;
                    for(int i = left + 1;iMAXnum) {MAXnum = nums[i],index = i;}
                    } 
                    MAXIndex.push(index);
                }

                if(nums[right]>nums[MAXIndex.top()]) {MAXIndex.pop();MAXIndex.push(right);}
            }
            else
            MAXIndex.push(right);
            cout<

程序一定要注意,最值要在范围内:

 Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第18张图片

 

下面是官方解法:

(官方解法:https://leetcode-cn.com/problems/sliding-window-maximum/solution/hua-dong-chuang-kou-zui-da-zhi-by-leetcode-3/) 

(C++版本:https://leetcode-cn.com/problems/sliding-window-maximum/solution/dan-diao-dui-lie-by-labuladong/)

本题总的来说,不算是滑动窗口,只是名字一样,实际方法是双项队列deque

为了降低时间复杂度,我们观察一下窗口移动的过程类似于队列出队入队的过程,每次队尾出一个元素,然后队头插入一个元素,求该队列中的最大值

每次的值都和队尾元素比较,将小的弹出,大的暂时放入,队首一直都是最大值(在滑动窗口范围内)

class Solution {
public:
    vector maxSlidingWindow(vector& nums, int k) {
        if(nums.empty()) return {};
        vectorRes;
        dequeMAXindex;
        //处理第一个窗口
        for(int i = 0;iright||MAXindex.front()

以上情况,正好是线性的时间复杂度 

面试题57 - II. 和为s的连续正数序列

https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第19张图片

算法参考:https://leetcode-cn.com/problems/he-wei-sde-lian-xu-zheng-shu-xu-lie-lcof/solution/shi-yao-shi-hua-dong-chuang-kou-yi-ji-ru-he-yong-h/

从头开始滑动窗口,找到解后,因为以这个为开头的解只可能有一个,所以左侧窗口边界收缩

这个方法非常巧妙,但是要注意细节,注意循环的break条件,否则会找不全内容

class Solution {
public:
    vector> findContinuousSequence(int target) {
        int Uplimit = target/2;
        //滑动窗口
         vector> Res;
        int left = 1,right = 1;
        int Sum = 0;
        while(left<=Uplimit)//小细节
        {
            if(Sum>target) 
            {
                Sum -= left;
                left++;
            }
            else if(SumTemp;
                for(int i = left;i

 

双指针

有下面一道题目,有一个排序数组,现在需要在线性的时间复杂度下,找出目标和,我们应该怎么做,Hash表是很好的方法

1. 两数之和 

https://leetcode-cn.com/problems/two-sum/

leetcode天字一号题目,两数之和,又回到了梦开始的地方

Hash表:

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        unordered_map m;
        for(int i = 0;i

 

但是稍稍改变题目,返回的是数组的元素而不是下标,双指针法也毫不逊色:

class Solution {
public:
    vector twoSum(vector& nums, int target) {
        int size = nums.size();
        sort(nums.begin(),nums.end());//排序
        vector Res;
        int left = 0,right = size-1;
        while(left0&&nums[right] == nums[right-1])right--;
                left++,right--;
            }
            else if((nums[left]+nums[right])>target) right--;
            else left++;
            
        }
        return Res;
        
    }
};

 

以上是一个典型的双指针技巧。

从排序数组的两端,不断向中间逼近,下面我们看图解:

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第20张图片

 

 

如果说快慢指针是数学问题,滑动窗口的一个非常傲娇的过程,先找到解,再将解移除区间,然后再次寻找解

双指针的核心,就是根据数列的性质(从小到大排序),让两个指针移动。

这些性质也就省去了很多冗余不必要的计算。

https://leetcode-cn.com/problems/3sum/solution/three-sum-ti-jie-by-wonderful611/

 

双指针一般出现在数组问题中,数组问题是个非常庞大的系列,其中使用到的方法也是各式各样。

15. 三数之和

 https://leetcode-cn.com/problems/3sum/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第21张图片

初见此题,在没有任何准备的情况下,暴力解法就是一种解法,在暴力解法的基础上,Hash表也是一个优化的选择。

但是面对数组问题,可以尝试排序后使用双指针的方法进行解决。

下面收录了一些非常好的解法和解题思路讲解:

https://leetcode-cn.com/problems/3sum/solution/san-shu-zhi-he-cshi-xian-shuang-zhi-zhen-fa-tu-shi/

https://leetcode-cn.com/problems/3sum/solution/man-hua-jue-bu-wu-ren-zi-di-xiang-kuai-su-kan-dong/

https://leetcode-cn.com/problems/3sum/solution/three-sum-ti-jie-by-wonderful611/

以上三篇文章,都是从不同的角度对本问题提出了分析的方法

变化太多的情况,逻辑上我们就要把他变成变化少的情况,固定一个数的位置,去找剩下两个数字。

class Solution {
public:
    vector> threeSum(vector& nums) {
        vector> Res;
        int size = nums.size();
        if(size <= 2) return Res;
        sort(nums.begin(),nums.end());
        int pre = 0,right = size-1,left = pre+1;
        while(pre<=size-3)
        {
            int sum = 0;
            
            while(left Temp;
                sum = nums[pre]+nums[left]+nums[right];
                if(sum == 0)
                {
                    Temp.push_back(nums[pre]);
                    Temp.push_back(nums[left]);
                    Temp.push_back(nums[right]);
                    left++,right--;
                }
                else if(sum>0) right--;
                else left++;
                if(!Temp.empty()) Res.push_back(Temp);

            }
            pre++,right = size-1,left = pre+1;
        }
        return Res;


    }
};

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第22张图片Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第23张图片

会发现,计算重复了,那么,我们需要剔除重复的结果

如果pre重复了,那么我们继续加,直到遇到一个新的pre

对于right和left也是一样的,既然重复,那么就不断循环,直到找到一个区别于刚才一组解的值

代码如下:

class Solution {
public:
    vector> threeSum(vector& nums) {
        vector> Res;
        int size = nums.size();
        if(size <= 2) return Res;
        sort(nums.begin(),nums.end());
        int pre = 0,right = size-1,left = pre+1;
        while(nums[0]<=0&&pre<=size-3)
        {
            int sum = 0;
            while(left Temp;
                sum = nums[pre]+nums[left]+nums[right];
                if(sum == 0)
                {
                    Temp.push_back(nums[pre]);
                    Temp.push_back(nums[left]);
                    Temp.push_back(nums[right]);
                    //位置一定要放对了,一定要是在等于0之后,剔除重复的内容
                    while(right>0&&nums[right] == nums[right-1]) right--;
                    while(left0) right--;
                else left++;
                if(!Temp.empty()) Res.push_back(Temp);

            }
            while(pre

版本二:

class Solution {
public:
    vector> threeSum(vector& nums) {
        if(nums.empty()) return {};
        int size = nums.size();
        vector>Res;
        sort(nums.begin(),nums.end());//排序
        int PCur = 0,left = 1,right = size-1;
        while(PCur<=size-2)//倒数第二个
        {
            if(PCur-1>=0&&nums[PCur] == nums[PCur-1]) {PCur++;continue;}
            //和自己的前一个比较,如果一样,那么我们就要跳过这个值
            left = PCur+1,right = size-1;
            while(left0)right--;
                else if(temp == 0)
                {
                    vector Vtemp;
                    Vtemp.push_back(nums[PCur]);
                    Vtemp.push_back(nums[left]);
                    Vtemp.push_back(nums[right]);
                    Res.push_back(Vtemp);
                    right--;left++;
                    //放置于正确的位置,当等于零时,移动左右指针,知道遇到不一样的内容为止,放置重复
                    while(right>=0&&nums[right] == nums[right+1]) right--;
                    while(left<=size-1&&nums[left] == nums[left-1]) left++;
                }

            }
            PCur++;
        }
        return Res;

    }
};

下面的解中,包含了各种个数的求和总结:

https://leetcode-cn.com/problems/3sum/solution/man-hua-jue-bu-wu-ren-zi-di-xiang-kuai-su-kan-dong/

那么将上面的题目稍作改变: 

16. 最接近的三数之和 

https://leetcode-cn.com/problems/3sum-closest/solution/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第24张图片

类比上一道题目。本题可以选择重复内容,只要求接近,那么我们不断去更新最小值,再根据目前三个数的和与target的关系,移动指针,不断去逼近正确答案。

class Solution {
public:
    int threeSumClosest(vector& nums, int target) {
        int size = nums.size();
        sort(nums.begin(),nums.end());
        if(size < 3) return 0;
        int Min = INT_MAX,pre = 0,right = size-1,left = pre+1,Res = 0;
        while(predelta) 
                {
                    Min = delta;
                    Res = sum;
                }
                if(sum

上面的题目是一个类型,那么当我们需要不能排序的情况,或者整体大小情况未知的时候,该怎么办?

9. 回文数

https://leetcode-cn.com/problems/palindrome-number/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第25张图片

灵活机动,将数字变成字符串,这样就可以诸位进行访问,然后双指针操作,一个从左一个从右开始,向中间收缩,一旦不相等,break; 

class Solution {
public:
    bool isPalindrome(int x) {
        string num = to_string(x);
        int size = num.size();
        int right = size-1,left = 0;
        while(left

11. 盛最多水的容器

https://leetcode-cn.com/problems/container-with-most-water/

(参考解答:https://leetcode-cn.com/problems/container-with-most-water/solution/on-shuang-zhi-zhen-jie-fa-li-jie-zheng-que-xing-tu/)

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第26张图片

暴力解法:

class Solution {
public:
    int maxArea(vector& height) {
        int size = height.size();
        if(size == 0) return 0;
        if(size == 2) 
        {
            return min(height[0],height[1]);
        }
        int pre = 0,right = size-1,left = pre+1,MAX = 0;
        while(pre

显然是超时,那么我们应该怎样解决这个问题呢?简化双指针

就两个指针,一左一右,计算面积,然后收缩,怎么收缩呢,收缩牵扯到长方形长边的缩短,所以要找出最大值,我们要固定right和left中的较大值,移动较小的值。

class Solution {
public:
    int maxArea(vector& height) {
        int left = 0, right = height.size() - 1;
        int Res = 0;
        while (left < right) {
            int sum = min(height[left], height[right]) * (right - left);
            Res = max(Res, sum);
            //移动小的边界
            if (height[left] <= height[right]) ++left;
            else right--;
        }
        return Res;
    }
};
// 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

(官方题解:https://leetcode-cn.com/problems/container-with-most-water/solution/sheng-zui-duo-shui-de-rong-qi-by-leetcode-solution/)

 

面试题 16.06. 最小差 

https://leetcode-cn.com/problems/smallest-difference-lcci/solution/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第27张图片

此题和上面的题目如出一辙,上面是没有办法排序,本次是没有办法知道两个数组彼此之间的大小情况

那么还是老办法,让他们自己排序,然后两个指针,各自指向各自的首地址,算完差值后,二者比较,大的肯定是动不了,大的动了二者差距越来越大,小的动,以此类推不断循环。

class Solution {
public:
    int smallestDifference(vector& a, vector& b) {
        int sizea = a.size(),sizeb = b.size();
        if(sizea == 0||sizeb == 0) return 0;
        sort(a.begin(),a.end());
        sort(b.begin(),b.end());
        long p1 = 0,p2 = 0,res = INT_MAX;
        while(p1

240. 搜索二维矩阵 II

 https://leetcode-cn.com/problems/search-a-2d-matrix-ii/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第28张图片

本题最大的技巧,是从右上角开始,因为如果从左上角开始,上面右面都是大值,这怎么移动?

反而是右上角a点为起始,比target大了,左移(因为a的值本行最大),比target小了,下移(因为a的值本行最小)

class Solution {
public:
    bool searchMatrix(vector>& matrix, int target) {
        if(matrix.empty()||matrix[0].empty()) return false;
        int m = matrix.size(),n = matrix[0].size();
        ;
        int Hbegin = 0,Lbegin = n-1;
        while(Hbegin=0)
        {
            if(matrix[Hbegin][Lbegin] == target) return true;
            if(matrix[Hbegin][Lbegin] > target) Lbegin--;
            else Hbegin++;
        }
        return false;
        
    }
};

 

面试题21. 调整数组顺序使奇数位于偶数前面

https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第29张图片

优秀代码:

class Solution {
public:
    vector exchange(vector& nums) {
        if(nums.empty()) return {};
        int size = nums.size();
        int left = 0,right = size - 1;
        while(left=0&&nums[right]%2==0) right--;//找最后一个奇数
            cout<<"right"<=size||left>right) break;
            swap(nums[left],nums[right]);
            left++,right--;
        }
        return nums;

    }
};

垃圾(但是实在是太容易想到了):

class Solution {
public:
    vector exchange(vector& nums) {
        if(nums.empty()) return {};
        int size = nums.size();
        vectorRes;
        unordered_mapM;
        for(int i = 0;i

 

1471. 数组中的 k 个最强值

https://leetcode-cn.com/problems/the-k-strongest-values-in-an-array/

周赛第二题:

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第30张图片

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第31张图片

本题叙述还是比较复杂的,我们整理一下内容:

  1. 排序
  2. 找中位数
  3. 寻找满足条件的数字

前两点非常好满足

第三点:我们看表达式一的样子,就是在求和中位数的差值(绝对值),越大越好,那么对于一个排序数组来说,首尾必然是差值最大的地方,但是具体差多少,是首差值大还是尾差值大,我们不知道

再看表达式二,当二者差值相等,本身值谁大选择谁,因为是排序数组,显然是序号越大,越靠近尾部的值大。

综上,直接双指针法,left = 0,right = size - 1

对比 abs(arr[right] - arr[mid]) 和 abs(arr[mid] - arr[left])的关系,大于等于,其实都是选择right值

只有当小于的时候,才会选择left,我们让while跳出条件为left = right即可。

class Solution {
public:
    vector getStrongest(vector& arr, int k) {
        if(arr.empty()) return {};
        sort(arr.begin(),arr.end());//排序
        //找中位数
        int size = arr.size();
        int mid = arr[size/2];
        //找到合适的内容
        vectorRes;
        int right = size-1,left  = 0;
        mid = left + (right - left)/2;
        while(left<=right)
        {
            if(k == 0) break;
            if(abs(arr[right] - arr[mid]) > abs(arr[mid] - arr[left])) //第一条规则
            {Res.push_back(arr[right]);right--;k--;}
            else if(abs(arr[right] - arr[mid]) == abs(arr[mid] - arr[left]))//第二条规则
            {Res.push_back(arr[right]);right--;k--;}//必然是后面的大
            else {Res.push_back(arr[left]);left++;k--;}
        }
        return Res;

    }
};

 

581. 最短无序连续子数组

https://leetcode-cn.com/problems/shortest-unsorted-continuous-subarray/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第32张图片

本题我们要的是双指针,从头开始,扫描,找单调递增中的异常,也就是下降沿

找到下降沿后,需要移动左指针,找到下降沿的起点,同时移动右指针,找到下降沿的终点

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第33张图片

class Solution {
public:
    int findUnsortedSubarray(vector& nums) {
        if(nums.empty()) return 0;
        int size = nums.size();
        int right = 1,left = 0,begin = 0;
        int NewB = size,NewE = 0;
        while(rightnums[right]) 
            {
                int Ltemp = left;
                while(Ltemp>=0)//检查左侧,有没有高的
                {
                    if(nums[Ltemp]>nums[right]) Ltemp--;
                    else break;
                }
                int Rtemp = right;
                while(Rtempnums[Rtemp]) Rtemp++;
                    else break;
                }
                
                NewB = min(NewB,Ltemp);NewE = max(NewE,Rtemp);//记录起点和终点
                right++;left++;
            }
        }

        return NewE==0?0:NewE-NewB-1;
    }
};

 

283. 移动零

https://leetcode-cn.com/problems/move-zeroes/

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第34张图片

双指针原地交换,并不难

class Solution {
public:
    void moveZeroes(vector& nums) {
        if(nums.empty()) return;
        int right = 1,left = 0;
        while(right1) left++;
                else {right++;left++;}
            }
            else {right++;left++;}
        }
        return;
    }
};

75. 颜色分类

https://leetcode-cn.com/problems/sort-colors/

三指针分类解题:

三个指针,C2探索2的左边界,C0探索0的右边界,PCur保存正常遍历

当PCur指向0时,将C0和PCur交换,二者同时增加步长

当PCur指向2时,将C2和PCur交换,此时收缩2的边界指针C2,也就是将C2--,但是此时PCur不能动,因为你不知道现在PCur指向的是谁,需要在下一轮循环中判断。

比如下面的情况

Leetcode双指针操作合集(快慢指针&滑动窗口&双指针)_第35张图片

错误程序: 

class Solution {
public:
    void sortColors(vector& nums) {
        //原地排序
        int C2 = nums.size()-1,C0 = 0,PCur = 0;
        while(PCur

本题要特别的注意细节问题:

class Solution {
public:
    void sortColors(vector& nums) {
        //原地排序
        int C2 = nums.size()-1,C0 = 0,PCur = 0;
        while(PCur<=C2)
        {
            if(nums[PCur]==0)//等于0
            {
                swap(nums[PCur],nums[C0]);
                C0++,PCur++;//交换完后,都移动
            }
            else if(nums[PCur]==2)//等于2
            {
                swap(nums[PCur],nums[C2]);
                C2--;//只减少2的边界               
            }
            else PCur++;
        }
    }
};

 

你可能感兴趣的:(LeetCode)