leetcode15三数之和,18四数之和

问题:

1、给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。(此处重复指三元组重复,而非三元组中的元素,下同)

2、给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target


三数之和

三数之和简要思路:
1.将待搜寻中的数组进行排序
2.a指代排序后的数组第一个元素,b指代第二个元素,c指代最后一个元素。(a,b,c均为数组下标)
3.保证a,b不动,因为此时a+b是最小的两个数之和,而c是最大数,如果a+b+c>target,将c左移,即减小c,直到bc相遇;=target即为所求,移动b,进行下一步;<target则说明此时移动c无用,需要移动b,进行下一步。
4.将b右移,但需要移动至下一个不同的数字(因为数组中可能存在多个相同的元素),所以需要确保此时

  • b移动了,即b>a+1
  • b换了一个数字,num[b]!=num[b-1]

重复3,直到b到达倒数第二个数时,此时下一步需移动a
5.将a右移,处理方式与b相同,直至a到达倒数第三个数。
此处官方将循环条件都设置为了<n,应为a<n-2,b<n-1,c<n,但时间复杂度均为n³,无伤大雅。

具体内容可看15官方解答

https://leetcode.cn/problems/3sum/solution/san-shu-zhi-he-by-leetcode-solution/

代码如下

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        // 枚举 a
        for (int first = 0; first < n - 2; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n - 2; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
                if (second == third) {
                    break;
                }
                if (nums[second] + nums[third] == target) {
                    ans.push_back({nums[first], nums[second], nums[third]});
                }
            }
        }
        return ans;
    }
};

若是改为三元数组中的元素也不同,则需修改判断条件
从开始就确定a位于一个数的最后位置
即当a<n-2(a最大取到n-2),确保a与后面一个数字不同,若相同则右移a。b在上一问已经确定了移动时必换数字,仅需保证初始时b与a不同即可,a又是所代表数字的最后位置,后一位数字必不同,故ab不同
若使用nums[first] == nums[first - 1],则a已经换数

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if ((first < n - 1) && nums[first] == nums[first + 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1;second < (n-1); ++second) {
                // 需要和上一次枚举的数不相同
                if ((second > first + 2) && nums[second] == nums[second + 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
                if (second == third) {
                    break;
                }
                if (nums[second] + nums[third] == target) {
                    ans.push_back({ nums[first], nums[second], nums[third] });
                }
            }
        }
        return ans;
    }
};

四数之和:

与三数同理,仅需在原c外层多进入一个与ab循环相似的c1,c1<n-1。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }

            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // d 对应的指针初始指向数组的最右端
                int fourth = n - 1;
                int mytarget = target-nums[first]-nums[second];
                for(int third=second+1;third<n;++third)
                {
                    if(third>second+1&&nums[third]==nums[third-1]){
                        continue;
                    }
                    // 需要保证 c 的指针在 d 的指针的左侧
                    while (third < fourth && nums[third] + nums[fourth] > mytarget) {
                        --fourth;
                    }
                    // 如果指针重合,随着 c后续的增加
                    // 就不会有满足 a+b+c+d=target 并且 c<d 的 d 了,可以退出循环
                    if (third == fourth) {
                        break;
                    }
                    if (nums[third] + nums[fourth] == mytarget) {
                        ans.push_back({nums[first], nums[second], nums[third],nums[fourth]});
                    }
                }
            }
        }
        return ans;
    }
};

确保四元数组元素不同与上同理

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if ((first <n-3) && nums[first] == nums[first + 1]) {
                continue;
            }

            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // d 对应的指针初始指向数组的最右端
                int fourth = n - 1;
                int mytarget = target - nums[first] - nums[second];
                for (int third = second + 1; third < n; ++third)
                {
                    if (third > second + 1 && nums[third] == nums[third - 1]) {
                        continue;
                    }
                    // 需要保证 c 的指针在 d 的指针的左侧
                    while (third < fourth && nums[third] + nums[fourth] > mytarget) {
                        --fourth;
                    }
                    // 如果指针重合,随着 c后续的增加
                    // 就不会有满足 a+b+c+d=target 并且 c<d 的 d 了,可以退出循环
                    if (third == fourth) {
                        break;
                    }
                    if (nums[third] + nums[fourth] == mytarget) {
                        ans.push_back({ nums[first], nums[second], nums[third],nums[fourth] });
                    }
                }
            }
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值