相向双指针

167. 两数之和Ⅱ-输入有序数组

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        vector<int> ans;
        int n = numbers.size();
        int l = 0, r = n - 1;
        while (l < r) {
            if ((numbers[l] + numbers[r]) == target) {
                ans.push_back(l+1), ans.push_back(r+1);
                break;
            } else if ((numbers[l] + numbers[r]) < target) {
                l++;
            } else r--;
        }
        return ans;
    }
};

15.三数之和

本题可以看作两数之和的升级版,即固定一个数不动,看另外两个数之和是否等于这个数的相反数。时间复杂度为 \(O(n^2).\) (枚举第一个数为 \(O(n)\),双指针为 \(O(n)\))。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());//先排序,方便尺取
        vector<vector<int>> ans;
        int n = nums.size();
        for (int i = 0; i < n - 2; i++) {
            int x = nums[i];
            if (i && x == nums[i - 1]) continue;//跳过重复数字(当前nums[i]已经被使用过)
            if (x + nums[i + 1] + nums[i + 2] > 0) break;//当x与其后的两个数之和大于0,说明这组无解(后面的数变大或x变大都不可能再等于0)
            if (x + nums[n - 2] + nums[n - 1] < 0) continue;//如果x与最后面的两个数之和小于0,说明后面两个数变小也仍然小于0,只有增大x。
            int l = i + 1, r = n - 1;
            while (l < r) {
                int sum = x + nums[l] + nums[r];
                if (sum > 0) r--;
                else if (sum < 0) l++;//尺取
                else {
                    ans.push_back({x, nums[l], nums[r]});
                    l++;//当前的l和r所指向的数下回不能再使用。
                    while (l < r && nums[l] == nums[l - 1]) l++;
                    r--;
                    while (l < r && nums[r] == nums[r + 1]) r--;
                }
            }
        }
        return ans;
    }
};

2824.统计和小于目标的下标对数目

class Solution {
public:
    int countPairs(vector<int>& nums, int target) {
        int n = nums.size();
        int l = 0, r = n - 1, ans = 0;
        sort(nums.begin(), nums.end());
        while (l < r) {
            int sum = nums[l] + nums[r];
            if (sum >= target) {
                r--;//nums[r]与下标i在[l, r-1]中的任何nums[i]相加都>=target
            } else if (sum < target) {
                ans += r - l;//nums[l]与下标i在[l+1, r]中的任何nums[i]相加都<target
                l++;
            } 
        }
        return ans;
    }
};

16.最接近的三数之和

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        int n = nums.size(), ans;
        int MIN = 0x3f3f3f3f;
        for (int i = 0; i < n - 2; i++) {
            int x = nums[i];
            if (i && x == nums[i - 1]) continue;
            int s = x + nums[i + 1] + nums[i + 2];
            if (s > target) {// 后面无论怎么选,选出的三个数的和不会比 s 还小
                if (s - target < MIN) {
                    MIN = s - target;
                    ans = s;
                }
                break;
            }

            s = x + nums[n - 2] + nums[n - 1];
            if (s < target) {
                if (target - s < MIN) {
                    MIN = target - s;
                    ans = s;
                }
                continue;
            }

            int l = i + 1, r = n - 1;
            while (l < r) {
                s = x + nums[l] + nums[r];
                if (s == target) {
                    return target;
                } else if (s > target) {
                    if (s - target < MIN) {
                        MIN = s - target;
                        ans = s;
                    } 
                    r--;
                } else {
                    if (target - s < MIN) {
                        MIN = target - s;
                        ans = s;
                    }
                    l++;
                }
            }
        }
        return ans;
    }
};

611.有效三角形的个数

错误解法:固定最小边

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int n = nums.size(), ans = 0;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < n - 2; i++) {
            int x = nums[i];
            int l = i + 1, r = n - 1;
            while (l < r) {
                if (x + nums[l] > nums[r]) {
                    ans += r - l;
                    l++;
                } else {
                    r--;
                }
            }
        }
        return ans;
    }
};

这里错误的原因主要是,在固定最小边 \(a\) 时,我们的目标是使得 a + b > c,但现在 \(a\) 在不等号左边,也就是当 \(a\) + \(b\) \(\leqslant\) \(c\) 时,不知道是 \(b\) 较小造成的还是 \(c\) 较大造成的。固定最大边就能保证一定是左边太小造成的。

正确解法:固定最大边

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int n = nums.size(), ans = 0;
        sort(nums.begin(), nums.end());
        for (int k = 2; k < n; k++) {
            int l = 0, r = k - 1;
            while (l < r) {
                int s = nums[l] + nums[r];     
                if (s > nums[k]) {
		    	    // nums[l]+nums[r] > c 同时意味着:
                    // nums[l+1]+nums[r] > c
                    // nums[l+2]+nums[r] > c
                    // ...
                    // nums[r-1]+nums[r] > c
                    // 从 l 到 r-1 一共 r-l 个
                    ans += r - l;
                    r--;
                } else {
		    	    // nums[l]+nums[r] <= c 同时意味着
                    // nums[l]+nums[r-1] <= c
                    // ...
                    // nums[l]+nums[l+1] <= c
                    // 所以在后续的内层循环中,nums[l] 不可能作为三角形的边长,没有用了
                    l++;
                }
            }
        }
        return ans;
    }
};

18.四数之和

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<vector<int>> ans;
        for (int i = 0; i < n - 3; i++) {
            long long x = nums[i];
            if (i && x == nums[i - 1]) continue;
            if (x + nums[i + 1] + nums[i + 2] + nums[i + 3] > target) break;
            if (x + nums[n - 1] + nums[n - 2] + nums[n - 3] < target) continue; 
            for (int j = i + 1; j < n - 2; j++) {
                int y = nums[j];
                if (j > i + 1 && y == nums[j - 1]) continue;
                if (x + y + nums[j + 1] + nums[j + 2] > target) break;
                if (x + y + nums[n - 2] + nums[n - 1] < target) continue;
                int l = j + 1, r = n - 1;
                while (l < r) {
                    long long s = x + y + nums[l] + nums[r];
                    if (s > target) r--;
                    else if (s < target) l++;
                    else {
                        ans.push_back({(int)x, (int)y, nums[l], nums[r]});
                        l++;
                        while (l < n && nums[l] == nums[l - 1]) l++;
                        r--;
                        while (r > j && nums[r] == nums[r + 1]) r--;
                    }
                }
            }
        }
        return ans;
    }
};

11.盛最多水的容器

哪边的木板短,就把哪边的木板去掉。(在中间的木板比短木板长或比短木板短都无法更新 \(\rm ans\)

class Solution {
public:
    int maxArea(vector<int>& h) {
        int n = h.size();
        int ans = 0;
        int l = 0, r = n - 1;
        while (l < r) {
            int x = r - l, y = min(h[l], h[r]);
            int s = x * y;
            ans = max(ans, s);
            if (h[l] <= h[r]) {
                l++;
            } else {
                r--;
            }
        }
        return ans;
    }
};

42. 接雨水

class Solution {
public:
    int trap(vector<int>& h) {
        int n = h.size();
        int pre = 0, suf = 0, ans = 0;
        int l = 0, r = n - 1;
        while (l < r) {
            pre = max(pre, h[l]);
            suf = max(suf, h[r]);
            if (pre <= suf) {
                ans += pre - h[l];
                l++;
            } else {
                ans += suf - h[r];
                r--;
            }
        }
        return ans;
    }
};
posted @ 2024-07-07 09:16  胖柚の工作室  阅读(21)  评论(0)    收藏  举报