day7 三数之和 | 四树之和

三数之和

区别于两数之和,这题使用哈希表就比较麻烦了,如果要使用哈希得存一个pair对吧,因为这个时候是三元了。
如何找到三元组呢?先固定一个,剩下变成了两数之和了,问题转化为,确定一个数字,在后面的区间寻找两个数字之和等于这个数字的相反数。
我们可以使用先排序后用双指针的方法去寻找这两个数字
另一个难点是去重,因为我们要找到是三元组i, j, k 也就是要保证i, j, k是不重复的

  • 第一个方面是针对原数组,也就是i,碰到相同的元素就跳过;
  • 第二个方面是针对j的如果nums[j]==nums[j+1]就跳过
  • 第三个方面是针对k的如果nums[k]==nums[k-1]就跳过
  • 一个剪枝,如果第一个指针指向的数字已经大于零了,则说明它后面的元素都是大于零的,不可能相加还等于0,提前break
if(nums[i] > 0 break;
while(l < r && nums[l] == nums[l + 1]) l++;   
while(l < r && nums[r] == nums[r - 1]) r--;

注意这个时候跳出循环是nums[l]!=nums[l+1] 这个时候在nums[l]==nums[l-1],也就是我们加入{l,r}的那个l,下一轮查找我们要排除这个l, r所以两个while去重之后我们还要继续调整一下指针,l++, r--,这里是为了区别于上一次我们已经加入的点集
完整代码+注释

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; ++i) {
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            if(nums[i] > 0) break;
            int l = i + 1, r = n - 1;
            int ta = -nums[i];
            while(l < r) {
                // [l, r)
                // find nums[l]+nums[r] == -nums[i]
                int sum = nums[l] + nums[r];
                if(sum > ta) {
                    r--;
                }
                else if(sum < ta) {
                    l++;
                }
                else {
                    // erase repeated element
                    ans.push_back({nums[i], nums[l], nums[r]});
                    while(l < r && nums[l] == nums[l + 1]) l++;
                    while(l < r && nums[r] == nums[r - 1]) r--;
                    l++, r--; // skip the tuple that we already used
                }
            }
        }
        return ans;
    }
};

可以看到剪枝的效果还是不错的

四数之和

思路上依然是先排序,但是剪枝操作需要调整。
先固定两个数字,然后使用双指针去寻找下面的两个数字:

class Solution {
public:
    typedef long L;
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        if(nums.size() < 4) {
            return {};
        }
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<vector<int>> ans;
        for(int a = 0; a < n;++a) {
            if(nums[a] > target && target > 0) {
                break; // if nums[a] > taget > 0 then nums[b] ... > taget > 0
            }
            if(a > 0 && nums[a] == nums[a - 1]) {
                continue; // erase reduplicate nums[a]
            }
            for(int b = a + 1; b < n; ++b) {
                if(nums[a] +  nums[b] > target && target > 0) {
                    break; // nums[a]+nums[b]>target>0 then all nums that behind nums[b] is bigger than target
                }
                if(b > a + 1 && nums[b] == nums[b - 1]) continue;
                int left = b + 1, right = n - 1;
                L ta = (L)target - nums[a] - nums[b];
                while(left < right) {
                    auto sum = L(nums[left])+L(nums[right]);
                    if(sum == ta) {
                        ans.push_back({nums[left], nums[right], nums[a], nums[b]});
                        // erase duplicate element
                        while(left < right && nums[left] == nums[left + 1]) left++;
                        while(left < right && nums[right] == nums[right - 1]) right--;
                        left++, right--;
                    }
                    else if(sum > ta) {
                        right--;
                    }
                    else {
                        left++;
                    }
                }
            }


        }
        return ans;
    }
};
posted @ 2024-01-04 15:17  ccnju  阅读(12)  评论(0)    收藏  举报