一、双指针算法题(上)

283. 移动零

题目要求:

leeetcode之双指针算法_双指针

思路:两层循环,第一层循环寻找0,第二层找非0,交换。两层循环,时间复杂度为O(leeetcode之双指针算法_对撞指针_02)

leeetcode之双指针算法_双指针_03


优化思路:双指针

cur先遍历数组,寻找非0元素,dest暂时按兵不动,dest到快指针中间的元素(左开右开),用来表示0元素,cur到数组结尾表示的是为探索区域,直到cur找到非0元素,交换0元素和非0元素,dest移动,数组开始到dest,左闭右开,表示 的是已完成区域。如下图

leeetcode之双指针算法_leetcode_04

用dest表示第一个0元素所在位置的前一个位置,dest初始化为-1,是因为不清楚0元素的位置

cur表示当前非0元素的下标,cur要遍历数组,所以初始化为0

不管如何,在cur找到非0元素时,[dest+1,cur-1]这一范围下标对应的一定是0元素,cur的目的就是寻找非0元素,找到之后,就和dest指向的0元素进行交换。

如果是暴力解法,第二层循环不可避免的会重复遍历。

双指针,cur指针就相当于暴力解法的第一层循环,dest指针相当于第二层,但是没有进行重复遍历(不会回退)

代码实现:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
     for(int dest=-1,cur=0;cur<nums.size();cur++)
     {
        if(nums[cur])
        {
            swap(nums[++dest],nums[cur]);
        }
     }
    }
};

1089. 复写零

题目要求:

leeetcode之双指针算法_双指针_05

思路:遍历数组,遇到0,先将0元素后的元素向后移动一位,再进行复写0,

移动时,需要从后向前,但是最后一个移动的元素的数组最后一个元素的前一个。

并且,在移动前,需要对寻找到的0元素下标进行判断,如果已经是最后一个元素,或者倒数第二个元素,将最后一个元素置为0后,复写0就结束了。

如果不是上述情况,才需要进行先移动,后复写的操作,如下图。

leeetcode之双指针算法_对撞指针_06

优化思路:双指针

0元素需要被复写,只要复写一次,原数组末尾的元素就需要去除。用cur指针遍历数组,找出原数组经过复写后的最后一个元素的下标。dest指针根据cur指针指向的元素不断++,非0元素+1,0元素+2,最后表示的是复写后新数组最后一个元素的下标。

如果0是数组中的最后一个元素,也即cur指向了0元素,此时加上该元素,数组已达上限,但是又需要复写,dest会出现越界的情况。

代码实现

class Solution {
public:
    void duplicateZeros(vector<int>& arr) {
        int cur=0,dest=-1;
        while(cur<arr.size())
        {
            if(arr[cur]==0) dest+=2;
            else dest+=1;
            if(dest>=arr.size()-1) break;
            cur++;
        }
        if(dest==arr.size())
        {
            arr[arr.size()-1]=0;
            dest-=2;
            cur-=1;
        }
        while(cur>=0)
        {
            if(arr[cur]==0)
            {
                arr[dest]=0;
                arr[dest-1]=0;
                dest-=2;
                cur--;
            }
            else{
                arr[dest]=arr[cur];
                cur--;
                dest--;
            }
        }

    }
};

202. 快乐数

题目要求:

leeetcode之双指针算法_对撞指针_07

解题思路:不断对一个数每个位置数字求平方和的过程中,一定会出现两次相同的结果,也即出现循环,判断循环出现的数是否为1,即可判断是否为快乐数字。

代码实现:

class Solution {
public:
   int seq(int n)
   {
        int sum=0;
        while(n)
        {
            int r=n%10;
            sum+=r*r;
            n/=10;
        }
        return sum;
   }
    bool isHappy(int n) {
        int slow=n,fast=seq(n);
        while(slow!=fast)//可能是为1,也可能是不为1,但二者终究会相同
        {
            slow=seq(slow);
            fast=seq(seq(fast));

        }
        return slow==1;
    }
};

11. 盛最多水的容器

题目要求:

leeetcode之双指针算法_快慢指针_08

leeetcode之双指针算法_leetcode_09

解题思路:两层循环,比较每一种结果,得出最大值。O(leeetcode之双指针算法_对撞指针_02)的时间复杂度,会超时。如下

leeetcode之双指针算法_leetcode_11

优化思路:对撞指针,O(n)的时间复杂度

容积等于长乘以宽,如果先让长度最大,那么在长度减小的过程中,容积要想更大,只能寻找更大的高度。在长度固定下,高度较低的下标开始移动,每一次移动,动要重新判断出谁更高谁更低,并且继续让较低的人移动一个坐标,每次移动都选取更大的容积。

代码实现:

class Solution {
public:
    int maxArea(vector<int>& height) {

        int less_hight=0;//记录较小的高度
        int maxarea=0;//记录容积
        for(int l=0,r=height.size()-1;l<r;)//对撞指针
        {
            if(height[l]>height[r])//选择高度较低的指针进行移动
            {
                less_hight=height[r];
                maxarea=max(maxarea,(r-l)*less_hight);//先记录当前较大容积
                r--;//再移动
            } 
            else 
            {
                less_hight=height[l];
                maxarea=max(maxarea,(r-l)*less_hight);
                l++;
            }

        }
        return maxarea;

    }
};

611. 有效三角形的个数

题目要求:

leeetcode之双指针算法_对撞指针_12

思路:三层循环,每层循环确定一条边,测试所有的可能,符合条件的让个数=1。会超时,如下图。

leeetcode之双指针算法_leetcode_13

优化思路:1.最小的两条边只要大于第三边,就一定构成三角形。2.对撞指针,利用一层for循环固定最大的一边,一个指针固定第二大的一边,另一个指针从最小的一个数开始寻找符合条件的数。

代码实现:

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        sort(nums.begin(),nums.end());//升序排序
        int size=0;//统计个数
        for(int i=nums.size()-1;i>=2;i--)//固定最大的一边
        {
            int r=i-1;//第二大的一边
            int l=0;//最小的一边
            while(l<r)//对撞指针固定剩余较小两边
            {
                if((nums[l]+nums[r])>nums[i])
                {
                	//最小的一边都符合条件,右侧的大于等于它的一定也满足
                    size+=(r-l);
                    r--;
                }
                else l++;//不满足需要更大的一边
            } 
        }
        return size;
    }
};