代码算法训练营day7 | 454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和

day7: 剩下的两题:

15. 三数之和

题目链接
状态:
文档:programmercarl.com

注意:
这和第一题中的四数相加Ⅱ很像,如果用哈希算法的思路就是:
两层for循环就可以确定 a 和b 的数值了,可以使用哈希法来确定 0-(a+b) 是否在 遍历数组里出现过,其实这个思路是正确的,但是我们有一个非常棘手的问题,就是题目中说的不可以包含重复的三元组。(i ≠ j ≠ k)

(哈希算法)思路:
首先创建一个二维数组,来存放符合条件的三元组。

因为要去重,不能出现像是:[-1,0,1] [0,1,-1]的情况,这样也算是重复。
所以要用一个 全局函数sort 进行排序,按照从小到大的顺序,就不会出现像上述一样的情况。
但是还要考虑一种去重:数组中有多个重复的元素。
这种去重就要通过剪枝来进行操作了。

代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[j], c = -(a + b)
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
            //短路条件
            if (nums[i] > 0) {
                break;
            }
            if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重
            //不可以让 nums[i] == nums[i+1] --> 这样会让后面的j=i+1 失去遍历一些值的机会
                continue;
            }
            unordered_set<int> set; //set有自动去重的效果
            //开始遍历第二个数
            for (int j = i + 1; j < nums.size(); j++) { 
                if (j > i + 2
                        && nums[j] == nums[j-1]
                        && nums[j-1] == nums[j-2]) { // 三元组元素b去重
                    continue;
                }
                int c = 0 - (nums[i] + nums[j]);
                if (set.find(c) != set.end()) {
                    result.push_back({nums[i], nums[j], c});
                    set.erase(c);// 三元组元素c去重
                } else {
                    set.insert(nums[j]);
                }
            }
        }
        return result;
    }
};

注意:
用哈希法的话去重会很麻烦,让人一下子想不到,所以选择用双指针的方式,来不断的寻找合适的值。

(双指针)思路:
思路图
拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。

接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。

如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        //创建一个二维数组reslt 存放最后结果
        vector<vector<int>> result;
        //排序
        sort(nums.begin(),nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for(int i = 0;i<nums.size();i++)
        {
            //排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if(nums[i] > 0)
            {
                return result;
            }
            //对a进行去重
            if(i>0 && nums[i] == nums[i-1])
            {
                //跳过这个重复的元素
                continue;
            }
            //排除万难 开始遍历
            int left = i+1;
            int right = nums.size()-1;
            while(right > left)
            {
                if(nums[i] + nums[left] + nums[right] > 0)
                {
                    right--;
                }
                else if(nums[i] + nums[left] + nums[right] < 0)
                {
                    left++;
                }
                else
                {
                    //结果==0,放入result中
                    result.push_back(vector<int>{nums[i],nums[left],nums[right]});
                    //进行b去重
                    while(right > left && nums[left] == nums[left+1])
                    {
                        left++;
                    }
                    //进行c去重
                    while(right > left && nums[right] == nums[right-1])
                    {
                        right--;
                    }

                    //找到答案后,双指针收缩
                    right--;
                    left++;
                }
            }

        }
        return result;
       
    }
};

18. 四数之和

题目链接
状态:有一半思路,没做出来
文档:programmercarl.com

思路:
和上一题有些类似,都是使用双指针的方法,先找前两个数,在用双指针去遍历后两个数。

首先创建一个二维数组,存放最后的结果。
再对数组进行一个 全局函数sort 排序操作,进行第一步去重。

先找第一个数 k,先剪枝(一级剪枝:对a),也就是判断短路条件:都得是正数才行。
再对 k 进行去重操作(一级去重,对a)

然后再去找第二个数 i,i=k+1,先剪枝(二级剪枝:对b),和一级剪枝一样,都得是正数才行。
再对 i 进行去重操作(二级去重,对b)

有了前两个数的和了,开始创建双指针,一个是left = i+1,一个是right = nums.size()-1。
开始while循环遍历,此时的思路和三数之和这道题的思路是一样的:
四数之和>tar ==>> 数加多了,右指针左移。
四数之和<tar ==>> 数加少了,左指针右移。
四数之和=tar ==>> 存入二维数组中,此时要对left 和 right 进行去重操作,去重完毕之后,双指针收缩,继续下一轮。

最后返回二维数组。

代码:

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        //二维数组,存放结果
        vector<vector<int>> result;
        //对数组进行排序
        sort(nums.begin(),nums.end());
        //四个数 k i left right
        for(int k = 0;k<nums.size();k++)
        {
            //一级剪枝:
            //首先对k进行剪枝:有不符合的情况直接就return
            //[-4 -1 0 5] tar=-5,so [-4 -1 0]就符合条件 但是-4 > target 
            //如果只判断这个条件的话 就会把正确的结果也剪掉了
            //nums[k] > target && target > 0 && nums[k] > 0
            //nums[k] > target && nums[k] >= 0
            //都是正数才行 才能满足这个短路条件
            if(nums[k] > target && target > 0 && nums[k] > 0 )
            {
                break;
            }
            
            //一级去重
            //k去重 要判断k>0,因为有一个k-1的操作
            if(k>0 && nums[k] == nums[k-1])
            {
                continue;
            }
            //遍历第二个数 i
            for(int i = k+1;i<nums.size();i++)
            {
                //二级剪枝
                //都得是正数才行
                //nums[k]+nums[i] > target && target > 0 && nums[k] + nums[i] > 0
                //nums[k]+nums[i] > target && nums[k] + nums[i] >= 0
                if(nums[k]+nums[i] > target && target > 0 && nums[k] + nums[i] > 0)
                {
                    break;
                }
                //二级去重
                if(i>k+1 && nums[i] == nums[i-1])
                {
                    continue;
                }
                //开始设置left right
                int left = i+1;
                int right = nums.size()-1;
                while(right > left)
                {
                    // nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
                    if((long)nums[k] + nums[i] + nums[left] +nums[right] > target)
                    {
                        //说明加多了
                        right--;
                    }
                    else if((long)nums[k] + nums[i] + nums[left] +nums[right] < target)
                    {
                        //说明加少了
                        left++;
                    }
                    else{
                        //==target
                        result.push_back(vector<int>{nums[k],nums[i],nums[left],nums[right]});
                        //对left去重
                        while(right > left && nums[left] == nums[left+1])
                        {
                            left++;
                        }
                        //对right去重
                        while(right > left && nums[right] == nums[right-1])
                        {
                            right--;
                        }

                        //找到答案时,left、right指针同时收缩
                        right--;
                        left++;

                    }
                }
            }
        }
        return result;
    }
};
  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
30个数学建模智能算法及MATLAB程序代码: chapter10基于粒子群算法的多目标搜索算法.rar chapter11基于多层编码遗传算法的车间调度算法.rar chapter12免疫优化算法在物流配送中心选址中的应用 .rar chapter13粒子群优化算法的寻优算法.rar chapter14基于粒子群算法的PID控制器优化设计.rar chapter15基于混合粒子群算法的TSP搜索算法 .rar chapter16 基于动态粒子群算法的动态环境寻优算法.rar chapter17基于PSO工具箱的函数优化算法.rar chapter18鱼群算法函数寻优.rar chapter19基于模拟退火算法的TSP算法.rar chapter1遗传算法工具箱.rar chapter20基于遗传模拟退火算法的聚类算法.rar chapter21模拟退火算法工具箱及应用.rar chapter22蚁群算法的优化计算——旅行商问题(TSP)优化 .rar chapter23基于蚁群算法的二维路径规划算法.rar chapter24 基于蚁群算法的三维路径规划算法.rar chapter25有导师学习神经网络的回归拟合——基于近红外光谱的汽油辛烷值预测.rar chapter26.rar chapter27无导师学习神经网络的分类——矿井突水水源判别.rar chapter28支持向量机的分类——基于乳腺组织电阻抗特性的乳腺癌诊断 .rar chapter29支持向量机的回归拟合——混凝土抗压强度预测.rar chapter2基于遗传算法和非线性规划的函数寻优算法 .rar chapter30极限学习机的回归拟合及分类.rar chapter3基于遗传算法的BP神经网络优化算法 .rar chapter4sa_tsp.rar chapter5基于遗传算法的LQR控制器优化设计.rar chapter6遗传算法工具箱详解及应用 .rar chapter7多种群遗传算法的函数优化算法.rar chapter8基于量子遗传算法的函数寻优算法 .rar chapter9基于遗传算法的多目标优化算法.rar

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值