球球速刷LC之双指针 二轮

无序两数之和

对于当前num[i],要找的是target-num[i],可利用集合逐渐构建搜索集合

class Solution {
public:
    //逐渐构造搜索集合 VS 初始时即构造完全的搜索集
    vector<int> twoSum(vector<int>& nums, int target) {
       vector<int> ret;
        
       if(nums.size()<=1) return ret;
        
        map<int,int>table;
       for(int i=0 ; i<nums.size();++i){
          if(table.count(target-nums[i])){
              ret.push_back(table[target-nums[i]]);
              ret.push_back(i);
              return ret;
          } 
          table[nums[i]]=i;
       }
        
        return ret;
    }
};
有序数字两数之和!!!

采用双指针

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

将原数列排序之后,归纳为有序三元组的解法。则依次选取第一个数字后,后面两数按照有序序列两数之和求解。注意去重

//归纳为有序三元组的解法
class Solution {
public:   
    vector<vector<int>> threeSum(vector<int>& nums) {
        
        sort(nums.begin(),nums.end());
        vector<vector<int>> result;
  
        //有序三元组第一个数字
        int n = nums.size();
        for(int i = 0 ;  i< nums.size();++i)
        {
            //第一个数字去重
            if(i > 0 && nums[i] == nums[i-1]) continue;
            if((i+2) < nums.size() && (nums[i] + nums[i+1] + nums[i+2] ) > 0 )break;
            //当前的第一个数字 跟最后两个数字之和也< target ,也就是当前数字太小,直接增大第一个数
            if(i < n-2 && (nums[i] + nums[n-1] + nums[n-2]) < 0) continue;
            
            //有序三元组剩余2个数
            int left = i+1;
            int right = nums.size()-1;
            while(left<right){
                int sum = nums[i] + nums[left] + nums[right];
                if(sum < 0) ++ left;
                else if(sum > 0) --right;
                else if(sum == 0){
                    result.push_back(vector<int>{nums[i], nums[left],nums[right]});
                    ++left;
                    --right;
                    while(left <right && nums[left] == nums[left-1]) ++left;
                    while(left < right && nums[right] == nums[right+1]) --right;
                }
            }
        }
        
        
        return result;
    }
};
最接近的3数字之和

思路同上一题,注意对最接近的判断。

class Solution {
public:   
    int twoSumCloset(int l,int r,vector<int>&nums,int target){
        int i=l,j=r;
        int ret=nums[i]+nums[j];
        while(i<j){
            int sum=nums[i]+nums[j];
            if(abs(sum-target)<abs(ret-target)){
                ret=sum;
            }
            if(sum<target) ++i;
            else if(sum>target)--j;
            else{
                break;
            }
        }
        return ret;
    }
    int threeSumClosest(vector<int>& nums, int target) {
        if(nums.size()<3) return -1;
        
        sort(nums.begin(),nums.end());
        
        int retsum=nums[0]+nums[1]+nums[2];
        for(int i=0;i<=nums.size()-3;++i){
            if(i>0&&nums[i]==nums[i-1])continue;
            int twosum = twoSumCloset(i+1,nums.size()-1,nums,target-nums[i]);
            int bias=abs(target-nums[i]-twosum);
            if(bias<abs(retsum-target)){
                retsum=twosum+nums[i];
            }
        }
        return retsum;
    }
};
四数之和

思路与三数之和一致。
注意与回溯法求K数之和不同处是,这里的数字有正有负,如果回溯需要对每一个数字均选择或不选择。而此处内层采用双指针方法,效率高,不会超时。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(),nums.end());
        
         vector<vector<int>> result;
        //有序四元组的第一个数
        for(int i = 0 ; i < nums.size() ;++i) {
            if(i > 0 && nums[i] == nums[i-1] ) continue;
            
            if((i+3) < nums.size() && (nums[i] + nums[i+1] + nums[i+2] + nums[i+3] ) > target ) break;
            
            //有序四元组的第二个数
            for(int j = i+1; j < nums.size() ;++j)
            {
               if(j > (i+1) &&  nums[j-1] == nums[j]) continue;
                
               if((j+2) < nums.size() && (nums[i] + nums[j] + nums[j+1] + nums[j+2] ) > target ) break;
                
                //有序四元组的最后两个数
                int left = j+1;
                int right = nums.size()-1;
                
                while(left < right)
                {
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if(sum > target) --right;
                    else if(sum < target) ++left;
                    else if(sum == target){
                       result.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});
                        ++left;
                        --right;
                       //跳过相同数
                       while((left < right) && (nums[left] == nums[left-1])) ++left;
                       while((left < right) && (nums[right] == nums[right+1])) -- right;
                    }
                }
                
            }
        }
        return result;
    }
};
最大装水体积!!!
    //对于一个木板对<i,j>其容积由较小者的高度决定,另一个较高者的高度即使更高也不影响。
    //对于木板i,其能组成的最大容器是与>=height[i] 中距离最远的那个木板组成。
    //因此,对于<i,j>中高度较小者A,其能组成的最大容积已经确定(由于i,j由两侧向内,假设A为i,此时j即为最远的那个>=height[i]的木板),因此此时可以直接跳过A。
class Solution {
public:
    int maxArea(vector<int>& height) {
        if(height.size()<2) return 0;
        
        int i=0;
        int j=height.size()-1;
        unsigned long area= (j-i)*min(height[i],height[j]);
        
        
        //对于一个木板对<i,j>其容积由较小者的高度决定,另一个较高者的高度即使更高也不影响。
        //对于木板i,其能组成的最大容器是与>=height[i] 中距离最远的那个木板组成。
        //因此,对于<i,j>中高度较小者A,其能组成的最大容积已经确定(由于i,j由两侧向内,假设A为i,此时j即为最远的那个>=height[i]的木板),因此此时可以直接跳过A。
        while(i<j){
            //对于i,j中较小者,其能组成的最大容积已经得到,可以跳过。
            area=max(area,(unsigned long)(j-i)*min(height[i],height[j]));
            if(height[i]<height[j]){
            
                ++i;
              
            }else{
               
                --j;
         
            }
            
        }
        return area;
    }
};
判断链表是否是回文子串

找到链表中点后,将链表后面一半翻转,在与前一半比较。

最长无重复字符子串!!!

这是一道DP题目。
假设dp[i] 以第i个字符结尾的最长长度,同时记录
s[i]上一次出现的位置。
若字符i第一次出现,则dp[i]=dp[i-1]+1
否则,最长长度不能超过到上一次出现的位置。因此,
dp[i]=min(dp[i-1]+1,i-last_pos);

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        vector<int>character_pos(256,-1);
        //dp[i] 以第i个字符结尾的最长长度
        vector<int>dp(s.size(),0);
        
        int ret=0;
        for(int i=0;i<s.size();++i){
            if(i==0) dp[i]=1;
            else{
                int last_pos = character_pos[s[i]];
                //该字符尚未出现过
                if(last_pos == -1){
                    dp[i]=dp[i-1]+1;
                }else{
                    dp[i]=min(dp[i-1]+1,i-last_pos);
                }
            }
            character_pos[s[i]]=i;
            if(dp[i]>ret)ret=dp[i];
        }
        return ret;
    }
};

双指针的滑动窗口问题
参考文章 : 滑动窗口框架

最短目标串
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值