leecode(15)三数之和

本文探讨了如何提高寻找数组中满足三元素之和为零问题的效率,从暴力法的O(n^3)优化到哈希表去重后的O(n^2),最终揭示双指针法的优雅解决方案,实现空间复杂度为O(1)。重点介绍了双指针法如何巧妙地避免重复三元组输出。
摘要由CSDN通过智能技术生成

2.2.1 题目说明
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

尝试:第一种暴力法

    public List<List<Integer>> threeSum1(int[] nums){
            int n = nums.length;
            // 定义结果列表
            List<List<Integer>> result = new ArrayList<>();
            // 三重for循环,枚举所有的三数组合
            for (int i = 0; i < n - 2; i++){
                for (int j = i + 1; j < n - 1; j++){
                    for (int k = j + 1; k < n; k++){
                        if (nums[i] + nums[j] + nums[k] == 0)
                            result.add( Arrays.asList(nums[i], nums[j], nums[k]) );
                    }
                }
            }
            return result;
        }

此种解法时间复杂度是o(n^3),空间复杂度为O(n),并且答案也无法满足题目找出不重复的三元组,这个结果是不去重的,同样的三元组在结果中无法排除.比如-1,0,1会出现两次。而且时间复杂度非常高是O(N)的三次方,接下来试图做些改进,降低复杂度,而且解决去重问题
尝试2:运用哈希表(要做去重,首先想到就是把结果保存到hash表里。仿照两数之和,直接存到HashMap中查找)

    public List<List<Integer>> threeSum2(int[] nums){
        int n = nums.length;
        List<List<Integer>> result = new ArrayList<>();
        HashMap<Integer,List<Integer>> map = new HashMap<>();

        for(int i =0;i<n;i++){
            int thatNum = 0 - nums[i];
            //如果已经存在了thatNum,就找到了一组解
            if(map.containsKey(thatNum)){
                ArrayList<Integer> list = new ArrayList<>(map.get(thatNum));
                list.add(nums[i]);
                result.add(list);
            }
            //如果没找到,就把当前两个值添加到map
            for(int j = 0;j < i;j++) {
                int newKey = nums[i] + nums[j];
                //如果key不存在,就直接添加进去
                if (!map.containsKey(newKey)) {
                    ArrayList<Integer> list = new ArrayList<>();
                    list.add(nums[i]);
                    list.add(nums[j]);
                    map.put(newKey, list);
                }
            }
        }
        return result;
    }

因为尽管通过HashMap存储可以去掉相同二元组的计算结果的值,但没有去掉重复的输出(三元组)。这就导致,0对应在HashMap中有一个值(0,List(0,0)),第三个0来了会输出一次,第四个0来了又会输出一次。如果希望解决这个问题,那就需要继续加入其它的判断来做去重,整个代码复杂度会变得更高。可以尝试用set去解一下,不过思维打开了,把复杂度降下去了,为O(n^2),最后经过我的思考,我终于发现了一个非常好的解法
解法:双指针法

    public List<List<Integer>> threeSum3(int[] nums){
        int n = nums.length;
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);//-4 -1 -1 0 1 2

        //1.遍历每个元素,找到三个数的最小的那个作为核心
        for(int i = 0 ;i < n;i++){
            // 1.1 如果当前数已经大于0,直接退出循环
            if (nums[i] > 0 ) break;
            // 1.2 如果当前数据已经出现过,直接跳过
            if ( i > 0 && nums[i] == nums[i-1] ) continue;

            // 1.3 常规情况,以当前数做最小数,定义左右指针
            int lp = i + 1;
            int rp = n - 1;
            //当左右指针遇到时,结束循环
            while(lp < rp){
                int sum = nums[i] + nums[lp] + nums[rp];
                if (sum == 0){
                    // 1.3.1 等于0,就是找到了一组解
                    result.add(Arrays.asList(nums[i], nums[lp], nums[rp]));
                    lp ++;
                    rp --;
                    // 如果移动之后的元素相同,直接跳过
                    while (lp < rp && nums[lp] == nums[lp - 1]) lp++;
                    while (lp < rp && nums[rp] == nums[rp + 1]) rp--;
                }
                // 1.3.2 小于0,较小的数增大,左指针右移
                else if(sum < 0)
                    lp ++;
                    // 1.3.3 大于0,较大的数减小,右指针左移
                else
                    rp --;
            }
        }
        return  result;
    }

时间复杂度为O(n^2),比暴力法有了很大的改善,空间复杂度仅为O(1),可以说双指针一种很巧妙非常优雅的设计.

最后,关于第一个暴力法我是在是想不出有什么办法可以去重,如果你们有什么好的想法,欢迎来讨论.至此结束,别忘记关注哦,算法日常更新思路.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhao_programmer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值