15.三数之和与18.四数之和

15. 三数之和

一开始看到这个题当然会想到经典的两数之和,那道题用的是哈希表,不过这道题和那道题有很大的不同,两数之和要求的是索引,且不存在重复的答案,而三数之和中非常重要的就是去重。

首先在不考率去重的情况下怎么做呢,当然可以直接暴力求解,时间复杂度为O(N ^ 3),如果有三指针的话时间复杂度会降低到O(N^2),如何用三指针呢,其实就是先排序,然后第一个指针从左往右遍历,每次遍历先固定好第一个指针,然后另设两个指针,一个指向末尾,一个指向第一个指针的下一个元素,然后这两个指针依次往中间逼近看能否得到满足要求的三个数,逼近的方法是当前三个指针指向元素的和若大于0则末尾指针左移,若小于0则左边的那个指针右移(因为排了序的所以这样移动可以往0逼近)。

那么如何去重呢,在上面我们已经排序了,那么相同的元素一定会紧挨在一起,我们要做的就是对每个指针跳过相同的元素,因为相同的元素只需要判断一次即可。

代码如下:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        vector<vector<int>> result;
        for (int i = 0; i < n - 2; i++) {
            if (i > 0 && nums[i] == nums[i - 1])
                continue;
            int left = i + 1, right = n - 1;
            while (left < right) {
                if (nums[i] + nums[left] + nums[right] > 0) right--;
                else if (nums[i] + nums[left] + nums[right] < 0) left++;
                else {
                    result.push_back({nums[i], nums[left], nums[right]});
                    while (left < right && nums[right] == nums[right - 1]) right--;
                    while (left < right && nums[left] == nums[left + 1]) left++;
                    right--;
                    left++;
                }
            }
        }
        return result;
    }
};

我们这里只在三数之和等于0的情况下去重,去重的步骤就是一直判断两边的指针是否与下一个相同,如果相同则跳过。其实还可以再加一个剪枝优化性能,即如果当前的nums[i]已经大于0,那么就可以直接break了,因为是排序后的数组,之后的数字以及和肯定都大于0了。

18. 四数之和

其实四数之和和三数之和的解法是差不多的,只不过多了一个循环,也可以说用的是四指针,在遍历的过程中固定两个指针,然后另外两个指针的行为就和三数之和差不多了。也可以使用剪枝,即当前和以及大于target并且和也大于0了,那么就可以直接break掉了(和必须也要大于0,因为如果小于0说明和还有可能继续变小以满足要求)。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int>> result;
        for (int i = 0; i < n - 3; i++) {
            if (i > 0 && nums[i] == nums[i - 1])
                continue;
            if (nums[i] > target && nums[i] > 0)
                break;
            for (int j = i + 1; j < n - 2; j++) {
                if (j > i + 1 && nums[j] == nums[j - 1])
                    continue;
                if (nums[i] + nums[j] > target && nums[i] + nums[j] > 0)
                    break;
                int left = j + 1, right = n - 1;
                while (left < right) {
                    if ((long)nums[i] + nums[j] + nums[left] + nums[right] > target) right--;
                    else if ((long)nums[i] + nums[j] + nums[left] + nums[right] < target) left++;
                    else {
                        result.push_back({nums[i], nums[j], nums[left], nums[right]});
                        while (left < right && nums[right] == nums[right - 1]) right--;
                        while (left < right && nums[left] == nums[left + 1]) left++;
                        right--;
                        left++;
                    }
                }
            }
        }
        return result;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值