双指针问题(中等难度)

4.Triplet Sum to Zero or Three sum(medium)

问题描述

给定一个未排序的数组,找出其中所有唯一的三元组加起来为零。
Example 1:

Input: [-3, 0, 1, 2, -1, 1, -2]
Output: [-3, 1, 2], [-2, 0, 2], [-2, 1, 1], [-1, 0, 1]
Explanation: There are four unique triplets whose sum is equal to zero.

Example 2:

Input: [-5, 2, -1, -2, 3]
Output: [[-5, 2, 3], [-2, -1, 3]]
Explanation: There are two unique triplets whose sum is equal to zero.

解题的关键:

这题挺经典
1.简单来说就是我们先确定一个数A,然后在剩下数里面找到两个数(B与C)来满足题目的要求(A+B+C=target)。但如果直接在剩下的数中找的话复杂度太高了,而且还存在重复的数的情况,所以先排序,然后再进行上述操作即可,后面的部分跟我们前面做的题目思路是一样的

vector<vector<int> > threeSum(vector<int> &num) {
    
    vector<vector<int> > res;

    sort(num.begin(), num.end());

    for (int i = 0; i < num.size(); i++) {
        //处理一下重复的数
        while (i + 1 < num.size() && num[i + 1] == num[i])  i++;
        int target = -num[i];
        int front = i + 1;
        int back = num.size() - 1;

        while (front < back) {
          }    
            int sum = num[front] + num[back];
            if (sum < target)
                front++;

            else if (sum > target)
                back--;
            else {
                vector<int> triplet = {num[i], num[front], num[back]};
                res.push_back(triplet);
                
                while (front < back && num[front] == triplet[1]) front++;

                while (front < back && num[back] == triplet[2]) back--;
            }            
        }

    return res;
    
}

5.Triplet Sum Close to Target (medium)

问题描述

给定一个未排序数的数组和一个目标数,在数组中找到一个和尽可能接近目标数的三元组,返回三元组的和。 如果有多个这样的三元组,则返回总和最小的三元组的总和。
Example 1:

Input: [-2, 0, 1, 2], target=2
Output: 1
Explanation: The triplet [-2, 1, 2] has the closest sum to the target.

Example 2:

Input: [-3, -1, 1, 2], target=1
Output: 0
Explanation: The triplet [-3, 1, 2] has the closest sum to the target.

Example 3:

Input: [1, 0, 1, 1], target=100
Output: 3
Explanation: The triplet [1, 1, 1] has the closest sum to the target.

解题的关键:

1.大体思路和上题是相同的,但是要有一个数来保存并更新与target最小的差距

static int searchTriplet(vector<int>& arr, int targetSum) {
    sort(arr.begin(), arr.end());
    int smallestDifference = numeric_limits<int>::max();
    for (int i = 0; i < arr.size() - 2; i++) {
      int left = i + 1, right = arr.size() - 1;
      while (left < right) {
     //计算和目标的差距
        int targetDiff = targetSum - arr[i] - arr[left] - arr[right];
        if (targetDiff == 0) {            
        //  如果找到三个数和目标相等,直接返回即可
          return targetSum;  
        }
	//更新最小的差距,注意我们需要返回的是差距最小的三个数的和
	//return target-最小的差距 = 差距最小的三个数和,所以要保证smallestDifference 是正数
        if (abs(targetDiff) < abs(smallestDifference) ||
              (abs(targetDiff) == abs(smallestDifference) && targetDiff > smallestDifference)) {
          smallestDifference = targetDiff; 
        }

        if (targetDiff > 0) {
          left++;  // we need a triplet with a bigger sum
        } else {
          right--;  // we need a triplet with a smaller sum
        }
      }
    }
    return targetSum - smallestDifference;
  }

6.Subarrays with Product Less than a Target (medium)

问题描述

给定一个具有正数和正目标数的数组,找到其乘积小于目标数的所有连续子数组。
Example 1:

Input: [2, 5, 3, 10], target=30 
Output: [2], [5], [2, 5], [3], [5, 3], [10]
Explanation: There are six contiguous subarrays whose product is less than the target.

Example 2:

Input: [8, 2, 6, 5], target=50 
Output: [8], [2], [8, 2], [6], [2, 6], [5], [6, 5] 
Explanation: There are seven contiguous subarrays whose product is less than the target.

解题的关键:

1.注意是连续的子数组,所以不可以排序了。这题有点滑动窗口的意思了,窗口内的数的积小于target,然后窗口内的数的子数组依次放入即可,但窗口必须要向前移动,窗口收缩的时机就是窗口内的数的积大于等于target,这就需要一个数来计算窗口内数的积,窗口收缩时还得将窗口头部的数除去
2.窗口内的数的连续子数组依次放入是比较棘手的地方,我也就直接说解法了,因为这种东西我也想不出来,嗯背吧:
当窗口为[1,5]时此时解应该是{[1],[5],[1,5]}
当窗口为[1,5,3]时此时解应该是{[1],[5],[1,5],[3],[5,3],[1,5,3]} 所以,窗口内每多一个数后,要从窗口尾部来开始将子数组从一个到n个放入结果中[3],[5,3],[1,5,3]

vector<vector<int>> findSubarrays(const vector<int>& arr, int target) {
    vector<vector<int>> result;
    double product = 1;
    int left = 0;
    for (int right = 0; right < arr.size(); right++) {
      product *= arr[right];
     //窗口缩小
      while (product >= target && left < arr.size()) {
        product /= arr[left++];
      }
      
      deque<int> tempList;
      //从窗口尾部开始,子数组的长度依次增大
      for (int i = right; i >= left; i--) {
        //保证顺序,所以得用双端队列,从头部插入
        tempList.push_front(arr[i]);
        vector<int> resultVec;
        //将deque中的数放入resulvec中
        move(begin(tempList), end(tempList), back_inserter(resultVec));
        result.push_back(resultVec);
      }
    }
    return result;
  }

7.Dutch National Flag Problem (medium)

给定一个包含 0、1 和 2 的数组,对数组进行就地排序。 您应该将数组的数字视为对象,因此,我们不能计算 0、1 和 2 来重新创建数组。

荷兰国旗由红、白、蓝三种颜色组成; 由于我们的输入数组也包含三个不同的数字,这就是为什么它被称为荷兰国旗问题。
Example 1:

Input: [1, 0, 2, 1, 0]
Output: [0, 0, 1, 1, 2]

Example 1:

Input: [1, 0, 2, 1, 0]
Output: [0, 0, 1, 1, 2]

解题的关键:

1.可以先思考一下,一个数轴,我们可以用两个点把数轴分成三个部分,这道题也是同理,我们用两个指针就可以区分这三种数,而且找到前两种,第三种自然也就分好了,用low ,high 两个指针处理low左边的小于1,high右边的大于1

 void sort(vector<int> &arr) {
    int low = 0, high = arr.size() - 1;
    for (int i = 0; i <= high;) {
      if (arr[i] == 0) {
        swap(arr, i, low);
        i++;
        low++;
      } else if (arr[i] == 1) {
        i++;
      } else {  // the case for arr[i] == 2
        swap(arr, i, high);
        // decrement 'high' only, after the swap the number at index 'i' could be 0, 1 or 2
        high--;
      }
    }
  }

7.Quadruple Sum to Target or 4Sum(medium)

问题描述

给出一个未排序的数组和一个目标数,找出其中所有唯一的四元数,其总和等于目标数。
Example 1:

Input: [4, 1, 2, -1, 1, -3], target=1
Output: [-3, -1, 1, 4], [-3, 1, 1, 2]
Explanation: Both the quadruplets add up to the target.

Example 2:

Input: [2, 0, -1, 1, -2, 2], target=2
Output: [-2, 0, 2, 2], [-1, 0, 1, 2]
Explanation: Both the quadruplets add up to the target.

解题的关键:

1.跟3sum一个思路,再加一层循环即可,两个循环确定两个数,双指针再确定一个数即可
2.重复数字处理不处理都可

vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> total;
        int n = nums.size();
        if(n<4)  return total;
        sort(nums.begin(),nums.end());
        for(int i=0;i<n-3;i++)
        {
            if(i>0&&nums[i]==nums[i-1]) continue;
            if(nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target) break;
            if(nums[i]+nums[n-3]+nums[n-2]+nums[n-1]<target) continue;
            for(int j=i+1;j<n-2;j++)
            {
                if(j>i+1&&nums[j]==nums[j-1]) continue;
                if(nums[i]+nums[j]+nums[j+1]+nums[j+2]>target) break;
                if(nums[i]+nums[j]+nums[n-2]+nums[n-1]<target) continue;
                int left=j+1,right=n-1;
                while(left<right){
                    int sum=nums[left]+nums[right]+nums[i]+nums[j];
                    if(sum<target) left++;
                    else if(sum>target) right--;
                    else{
                        total.push_back(vector<int>{nums[i],nums[j],nums[left],nums[right]});
                        do{left++;}while(nums[left]==nums[left-1]&&left<right);
                        do{right--;}while(nums[right]==nums[right+1]&&left<right);
                    }
                }
            }
        }
        return total;
    }

8.Comparing Strings containing Backspaces (medium)

问题描述

给出两个含有退格键的字符串(用字符’#'标识),检查这两个字符串是否相等。

Example 1:

Input: str1="xy#z", str2="xzz#"
Output: true
Explanation: After applying backspaces the strings become "xz" and "xz" respectively.

Example 2:

Input: str1="xy#z", str2="xyz#"
Output: false
Explanation: After applying backspaces the strings become "xz" and "xy" respectively.

Example 3:

Input: str1="xp#", str2="xyz##"
Output: true
Explanation: After applying backspaces the strings become "x" and "x" respectively.
In "xyz##", the first '#' removes the character 'z' and the second '#' removes the character 'y'.

Example 4:

Input: str1="xywrrmp", str2="xywrrmu#p"
Output: true
Explanation: After applying backspaces the strings become "xywrrmp" and "xywrrmp" respectively.

解题的关键:

1,直观来看,从前往后处理退格不方便,可以从尾部往前处理,而且不一定只有一个位置出现退格,所以要循环处理,不能只处理一次。从尾部开始碰到#开始计数,然后退格

int backspace(const string &str,int pos)
{
    int count=0;
    while(pos>=0)
    {
        if(str[pos]=='#') ++count;
        else if(count >0)  count--;
        else
        {
            break;
        }
         --pos;
    }
    return pos;
   
}
bool comp(const string &str1,const string &str2)
{
    int first =str1.size()-1;
    int second = str2.size()-1;
    while(first>=0 && second>=0)
    {
       int l1=backspace(str1,first);
       int l2=backspace(str2,second);
       //先处理后比较
       if(l1<0 && l2<0) return true;
       if(l1<0 && l2>0) return false;
       if(l1>0 && l2<0) return false;
       if(str1[l1]!=str2[l2]) return false;
       first = l1-1;
       second = l2-1;
    }
    return true;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值