bookstack

双指针

Two Sum

LeetCode/力扣

  • 先排序
  • 排序完,用两个指针分别指向第一个和最后一个
  • 根据和,分别移动前后指针

上述方法需要额外保存排序前的位置,所以可解,但不推荐,现给出排序好的解法(不是当前题目解法)

vector<int> twoSum(vector<int>& nums, int target) {
    int n = nums.size();
    vectort<int> res;
    for(int i = 0, j = n - 1; i < j;) {
        int t = nums[i] + nums[j];
        if(t > target) {
            j--;
        } else if(t < target) {
            i++;
        } else {
            res.push_back(i);
            res.push_back(j);
            break;
        }
    }
    return res;
}
  • 用map保存已经计算过数值位置
  • 如果找到target - 当前数在map中,直接输出结果
  • 如果不存在,则把结果放入map中

    vector<int> twoSum(vector<int>& nums, int target) {
    int n = nums.size();
    vector<int> res;
    map<int, int> nmap;
    
    for(int i = 0; i < n; i++) {
        int t = target - nums[i];
        if(nmap.find(t) != nmap.end()) {
            res.push_back(nmap[t]);
            res.push_back(i);
            break;
        }
        nmap[nums[i]] = i;
    }
    return res;
    }
    

Container With Most Water

LeetCode/力扣

  • 前后指针遍历
  • 哪边数字小,移动哪边

    int maxArea(vector<int>& height) {
    int res = -1, i = 0, j = height.size() - 1;
    while(i <= j) {
        int area = (j - i) * min(height[i], height[j]);
        res = max(res, area);
        if(height[i] < height[j]) {
            i++;
        } else {
            j--;
        }
    }
    return res;
    }
    

3Sum

LeetCode/力扣

  • 分别用两个数组保存出现的次数和去重后排序的数组
  • 然后计算最终是否满足条件
  • 也可以用map来保存上述两个值,过程是一样的

    vector<vector<int>> threeSum(vector<int>& nums) {
    vector<vector<int>> res;
    if(nums.size()==0) return res;
    int max=0, min=0;
    // 这里是计算数组的最大值和最小值
    for(int d:nums){
      max=max<d?d:max;
      min=min>d?d:min;
    }
    // 下面这些是计算最大最小值里面的这个数字出现了多少次,比如
    // nums = [-1,0,1,2,-1,-4], tb=[1,0,0,2,1,1,1]
    int tb_size=max-min+1, m=-min;
    vector<char> tb(tb_size, 0);
    for(int d:nums){
      if(++tb[d+m]>3)tb[d+m]=3;
    }
    if(tb[m]==2) tb[m]=1;
    // 下面这些是计算不重复的数组并排序过的,比如
    // nums = [-1,0,1,2,-1,-4], v= [-4,-1,0,1,2]
    vector<int> v;
    for(int i=0;i<tb_size;++i)
      if(tb[i]>0) v.push_back(i-m);
    int vsz=size(v);
    // 下面这些我没看懂
    for(int i=0;i<vsz;++i){
      int vi=v[i];
      if(vi>0) break;
      for(int j=i;j<vsz;++j){
          int vj=v[j];
          int vk=-vi-vj;  // 求相反数
          if(vk<vj) break;   //说明第三个数已经到第二个数后面去了,所以直接break
          if(vk>max||tb[vk+m]==0) continue;  //说明相反数在里面并不存在,直接进行下一次循环
          if(tb[vj+m]>1||vi<vj && vj<vk) res.push_back({vi,vj,vk}); // 求出的结果是否合法
      }
    }
    return res;
    }
    

3Sum Closest

LeetCode/力扣

  • 第一个从0~n遍历,第二个从i~n遍历
  • 如果sum大于target, 则右指针移动
  • 如果sum小于target,则左指针移动

    int threeSumClosest(vector<int>& nums, int target) {
    std::sort(nums.begin(), nums.end());
    int n = nums.size();
    if(n < 3) return 0;
    int res = nums[0] + nums[1] + nums[2];
    for(int i = 0; i < n; i++) {
        int j = i + 1, k = n - 1;
        while(j < k) {
            int sum = nums[i] + nums[j] + nums[k];
            res = abs(target - sum) < abs(res - target) ? sum : res;
            if(sum < target) {
                j++;
            } else if(sum > target) {
                k--;
            } else {
                res = target;
                return res;
            }
        }
    }
    return res;
    }
    

Trapping Rain Water

LeetCode/力扣

  • 只要左右都有黑色方块,那么积水一直会增加
  • 所以可以用双向指针解决问题

    int trap(vector<int>& height) {
    int res = 0, i = 0, j = height.size() - 1;
    int lmax = 0, rmax = 0;
    while(i < j) {
        if(height[i] < height[j]) {
            height[i] >= lmax ? (lmax = height[i]) : res += (lmax - height[i]);
            i++; 
        } else {
            height[j] >= rmax ? (rmax = height[j]) : res += (rmax - height[j]);
            j--;
        }
    }
    return res;
    }
    
  • 动态规划

  • 从左到右,找到当前最大值left_max

  • 从右到左,找到当前最大值right_max

  • 遍历,找到left_maxright_max最小值,减去height就是积水

    int trap(vector<int>& height) {
    if(height.size() == 0) return 0;
    int res = 0, n = height.size();
    vector<int> left_max(n, 0), right_max(n, 0);
    left_max[0] = height[0];
    for(int i = 1; i < n; i++) {
        left_max[i] = max(height[i], left_max[i - 1]);
    }
    right_max[n - 1] = height[n - 1];
    for(int i = n - 2; i >= 0; i++) {
        right_max[i] = max(right_max[i + 1], height[i]);
    }
    
    for(int i = 1; i < n - 1; i++) {
        res += min(left_max[i], right[i]) - height[i];
    }
    return res;
    }
    

Sort Colors

LeetCode/力扣

  • 分别用前后指针代表0和2所在的位置,然后遍历中间的
  • 如果是0, 跟左指针换
  • 如果是2, 跟右指针换

    void sortColors(vector<int>& nums) {
    int n = nums.size(), n0 = 0, n2 = n - 1;
    int i = 0;
    while( i <= n2) {
        if(nums[i] == 0 && i != 0) {
            swap(nums[i], nums[n0++]);
        } else if(nums[i] == 2 && i != n2) {
            swap(nums[i], nums[n2--]);
        } else {
            i++;
        }
    }
    }
    

Minimum Window Substring

LeetCode/力扣

  • 双指针,先后两个指针
  • 先移动右指针,用一个vector或map保存字符的个数
  • 当所有字符全部存在时,移动左指针
  • 直到不满足条件时,跳出循环

    string minWindow(string s, string t) {
    vector<int> count(128, 0);
    for(char c : t) count[c]++;
    
    int remain = t.size(), left = 0, right = 0, minStart = 0, minLen = INT_MAX;
    
    while(right < s.size()) {
        count[s[right]]--;
        if(count[s[right]] >= 0)remain--;
        right++;
        while(remain == 0) {
            if(right - left < minLen) {
                minStart = left;
                minLen = right - left;
            }
            count[s[left]]++;
            if(count[s[left]] > 0) remain++;
            left++;
        }
    }
    return minLen < INT_MAX ? s.substr(minStart, minLen) : "";
    }
    

Remove Duplicates from Sorted List

LeetCode/力扣

  • 一个指向当前的不重复尾部
  • 一个指向下一个遍历
  • 如果相等, 则 left -> next = right -> next
  • 否则 left = right

    ListNode* deleteDuplicates(ListNode* head) {
    if(head == nullptr) return head;
    ListNode* left = head;
    ListNode* right = head->next;
    
    while(right != nullptr) {
        if(left -> val == right -> val) {
            left->next = right->next;
        } else {
            left = right;
        }
        right = right->next;
    }
    return head;
    }
    

Subarray Product Less Than K

LeetCode/力扣

  • 用一个数字保存乘积,先移动右指针
  • 当乘积大于K时,一直除左边的
  • 直到程序小于K或left = right
  • 每次结果是left - right + 1

    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
    if(k == 0) return 0;
    int size = nums.size(), mul = 0, left = 0, right = 0, res = 0;
    
    while(right < size) {
        mul *= nums[right];
        while(mul >= k && left <= right) {
            mul / = nums[left++];
        }
        res += right - left + 1;
        ++right;
    }
    return res;
    }
    

Backspace String Compare

LeetCode/力扣

  • 从后面开始遍历,用一个数字记录当前space的个数
  • 然后保存新的字符串即可

    string backspace(string str) {
    string res = "";
    int count = 0;
    
    for(int i = str.size() - 1; i >= 0; i--) {
        if(str[i] == '#') {
            count++;
            continue;
        } 
        if(count > 0 ) {
            count--;
        } else {
            res = str[i] + res;
        }
    }
    return res;
    }
    
    bool backspaceCompare(string S, string T) {
    return backspace(S) == backspace(T);
    }
    

Squares of a Sorted Array

LeetCode/力扣

  • 先找到大于等于0的分界点,然后计算平方值
  • 如果左边大,则压入右边,右边++
  • 如果右边大,则压入左边,左边–
  • 最后把剩下的全都计算平方加进去

    vector<int> sortedSquares(vector<int>& A) {
    vector<int> res;
    int more = 0, less = 0;
    for(int i = 0; i < A.size(); i++) {
        if(A[i] >= 0 ) {
            more = i;
            less = i - 1;
            break;
        }
    }
    while(less >= 0 && more < A.size()) {
        int m = A[less] * A[less];
        int n = A[more] * A[more];
        if(m > n) {
            res.push_back(n);
            more++;
        }
        if(m < n) {
            res.push_back(m);
            less--;
        }
        if(m == n) {
            res.push_back(m);
            if(more != less) res.push_back(n);
            more++;
            less--;
        }
    }
    while(less >= 0) {
        res.push_back(A[less] * A[less]);
        less--;
    }
    while(more < A.size()) {
        res.push_back(A[more] * A[more]);
        more++;
    }
    return res;
    }