LeetCode热题100——双指针

提示:将数组分区,左边为非0区

如何分区?——》首先找到分区的界限(初始时第一个0的位置),然后在其右边,找到非0值就交换,再让边界往右移动一格

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int n=nums.size();
        int left=0,right=0;
        //找到第一个0的位置
        while(left<n){
            if(nums[left]==0){
                break;
            }
            left++;
        }
        //从第一个0的位置后开始查找,找到非0值交换
        right=left+1;
        while(right<n){
            if(nums[right]){
                swap(nums[left],nums[right]);
                //边界+1
                left++;
            }
            right++;
        }
    }
};

提示:类似于木桶,高度由低的那一边决定

首先初始化left=0(代表左边木板的下标)right=len-1(代表右边木板的下标),核心思想就是往里移动较低的木板,有可能遇到高一点的使得高度增加,此时再判断更新答案

class Solution {
public:
    int maxArea(vector<int>& height) {
        int length = height.size();
        int left=0,right=length-1,h,max=0;
        while(left<right){
            h=height[left]>height[right]?height[right--]:height[left++];
            int area=h*(right-left+1);
            if(area>max){
                max=area;
            }
        }
        return max;
    }
};

提示:先将数组排序, 然后遍历,只有当一个数<=0,才有可能从后面找两个数使得三树之和为0

题目不要求不能改变原数组,那么显然将数组排序后更容易做,首先将数组进行排序,然后遍历,对于每一个数,若其本身已经大于0,那么就不可能从它后面再找两个数使得三数之和为0。对于以某一个数为起点的子数组,采用双指针的方法来锁定三数之和。

假设遍历到了某一个数i,我们先固定其为三个数之中的一个,然后令left=i+1,right=len-1,使剩余的两个数搜索范围为i后面的所有数,然后令sum=三数之和,若sum>0,代表和太大了,right往左移,若sum<0,代表和太小了,left往右移,sum=0则加入结果集

注意:遍历的时候,若nums[i]==nums[i-1],那么结果集里面会有重复的答案出现,所以相等的话跳过这个就可以了,对于left和right也是一样的,若恰好sum=0,然后nums[left]==nums[left+1] && nums[right]==nums[right-1],那么会存入一个重复解

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> res;
        int len=nums.size();
        int sum=0;
        int flag=INT_MIN;
        int preLeft,preRight;
        for(int i=0;i<len;i++){
            if(nums[i]>0)break;//你自己已经大于0,后面全是大于0
            if(nums[i]==flag){
                continue;//如果碰到跟前面一样的,就直接跳过,避免重复
            }
            flag=nums[i];
            int left=i+1,right=len-1;
            preLeft=INT_MIN;
            preRight=INT_MIN;
            while(left<right){
                //left和right也要避免重复
                if(nums[left]==preLeft){
                    left++;
                    continue;
                }
                else if(nums[right]==preRight){
                    right--;
                    continue;
                }
                sum=0;
                sum+=nums[i]+nums[left]+nums[right];
                if(sum>0){
                    //太大了,right往左移
                    preRight=nums[right];
                    right--;
                }
                else if(sum<0){
                    //太小了,left往右移
                    preLeft=nums[left];
                    left++;
                }
                else {
                    res.push_back({nums[i],nums[left],nums[right]});
                    preRight=nums[right];
                    preLeft=nums[left];
                    left++;
                    right--;
                }
            }
        }
        return res;
    }
};

 提示:感觉上还是木桶

既然是木桶,相对于上面的题目,我们可以先找到一个最高的墙,作为固定的一个木板,然后通过这个木板将整块区域分成两个桶。

对于左边的区域,我们发现,只要某一个墙nums[i]比其右边的墙nums[j]高,就会有nums[i]-nums[j]的水在nums[j]的上面。那么我们可以设curr=nums[0]为初始的墙,然后往右边遍历,只要小于curr,那么就可以添加水的体积,大于则更新curr。

右边区域同理,只不过是从右往左走

class Solution {
public:
    int trap(vector<int>& height) {
        //先找到最高的柱子,然后从前面到这跟柱子,还有后面到这跟柱子,都遵循着一个装雨水的规律
        //从第一根(最后一根)柱子出发,这跟柱子的高度设为curr,比他低的就可以计算差值来获得雨水,遇到高的就重置curr,然后循环
        int res=0;
        int tallestIndex=0;
        for(int i=0;i<height.size();i++){
            if(height[i]>height[tallestIndex]) tallestIndex=i;
        }

        int curr=height[0];
        for(int i=1;i<=tallestIndex;i++){
            if(height[i]>curr) curr=height[i];
            else res+=(curr-height[i]);
        }

        curr=height[height.size()-1];
        for(int i=height.size()-2;i>=tallestIndex;i--){
            if(height[i]>curr) curr=height[i];
            else res+=(curr-height[i]);
        }
        return res;
    }
};

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值