刷题笔记--三数之和系列

题目1–15:三数之和

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。
示例:

给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

来源:力扣(LeetCode)

思路:

主要还是数组遍历的思想
1:首先对数组进行排序,
2:排序后固定一个数nums[i],再使用左右指针指向 nums[i]后面的两端,数字分别为 nums[L] 和 nums[R],
3:计算三个数的和 sumsum 判断是否满足为 0,满足则添加进结果集
如果 nums[i]大于 0,则nums[L] 和 nums[R]必然也是大于0的数,三数之和必然无法等于 0,结束循环
如果 nums[i]= nums[i-1],则说明该数字重复,会导致结果重复,所以应该跳过,i++
当 sum0 时,nums[L] =nums[L+1] 则会导致结果重复,应该跳过,L++
当 sum=0 时,nums[R]=nums[R−1] 则会导致结果重复,应该跳过,R–
时间复杂度:O(n^2),n 为数组长度

代码:

class Solution {
    public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList();
        int len = nums.length;
        if(nums == null || len < 3) return ans;
        Arrays.sort(nums); // 排序
        for (int i = 0; i < len ; i++) {
            if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
            if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
            int L = i+1;
            int R = len-1;
            while(L < R){
                int sum = nums[i] + nums[L] + nums[R];
                if(sum == 0){
                    ans.add(Arrays.asList(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--;
                }
                else if (sum < 0) L++;
                else if (sum > 0) R--;
            }
        }        
        return ans;
    }
}


题目2–16:最接近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

来源:力扣(LeetCode)

思路:

本题和上一题的区别就在于判定条件发生了变化。原来是三数之和=target ,现在是需要遍历所有的情况,找出最接近的那一组数。

首先还是先进行数组排序,时间复杂度 O(nlogn)
在数组 nums 中,进行遍历(可以只遍历到nums.length-2,因为当i=nums.length-2时,start =end),每遍历一个值利用其下标i,形成一个固定值 nums[i]
然后同样的,再使用前指针指向 start = i + 1 处,后指针指向 end = nums.length - 1 处,也就是结尾处
根据 sum = nums[i] + nums[start] + nums[end] 的结果,判断 sum 与目标 target 的距离,如果更近则更新结果 ans
同时判断 sum 与 target 的大小关系,因为数组有序,如果 sum > target 则 end–,如果 sum < target 则 start++,如果 sum == target 则说明距离为 0 直接返回结果
整个遍历过程,固定值为 n 次,双指针为 n 次,时间复杂度为 O(n^2)
总时间复杂度:O(n^2)

代码:

class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int ans = nums[0] + nums[1] + nums[2];
        int len = nums.length-2;
        for(int i=0;i<len;i++) {
            int start = i+1, end = nums.length - 1;
            while(start < end) {
                int sum = nums[start] + nums[end] + nums[i];
                if(Math.abs(target - sum) < Math.abs(target - ans))//若当前结果更接近target
                    ans = sum;
                if(sum > target)
                    end--;
                else if(sum < target)
                    start++;
                else
                    return ans;
            }
        }
        return ans;
    }
}

题目3–259:较小的三数之和

给定一个长度为 n 的整数数组和一个目标值 target,寻找能够使条件 nums[i] + nums[j] + nums[k] < target 成立的三元组 i, j, k 个数(0 <= i < j < k < n)。

示例:
输入: nums = [-2,0,1,3], target = 2
输出: 2
解释: 因为一共有两个三元组满足累加和小于 2:
[-2,0,1]
[-2,0,3]

来源:力扣(LeetCode)

思路:

刚开始一上来,发现这和上一步也就是判定条件不一样嘛,于是我就很简单地该了上一题的代码,自信满满地运行,发现竟然是错了,错误代码如下:

    public int threeSumSmaller(int[] nums, int target) {
        int num = 0;
        Arrays.sort(nums);    
        for(int i=0;i<nums.length;i++) {
            int start = i+1, end = nums.length - 1;
            while(start < end) {
                int sum = nums[start] + nums[end] + nums[i];
                if(sum >= target)
                    end--;
                else if(sum < target) {
                	start++;
                    num++;
                }     
              
            }
        }
        return num;
    }

我们来看这个判断语句:

  else if(sum < target) {
                	start++;
                    num++;
                }  

在之前的题目,为了令sum=target 或者sum与target最接近,因此,当sum < target时,我们向右移动start指针,使得sum大一点,以达到我们的目的。但这里,当sum >=target时,我们希望sum再小一点,sum < targe同样我们也希望sum再小一点,或者说我们发现一个问题就是,如果当前的start和end指针所在位子sum<target,那么当end指向这中间end-stat-1个任意一个数,sum<target都应该成立。因此,上面的代码会漏掉一些情况,这段代码应该为

else if(sum < target) {
                    num++;
                    num+=end-start-1;
                    start++;
                }     
              

整个遍历过程,固定值为 n 次,双指针为 n 次,时间复杂度为 O(n^2)
总时间复杂度:O(n^2)

最终代码如下:

public int threeSumSmaller(int[] nums, int target) {
        int num = 0;
        Arrays.sort(nums);    
        for(int i=0;i<nums.length;i++) {
            int start = i+1, end = nums.length - 1;
            while(start < end) {
                int sum = nums[start] + nums[end] + nums[i];
                if(sum >= target)
                    end--;
                else if(sum < target) {
                    num++;
                    num+=end-start-1;
                    start++;
                }     
              
            }
        }
        return num;
    }
}
发布了18 篇原创文章 · 获赞 1 · 访问量 2307

猜你喜欢

转载自blog.csdn.net/Better_WZQ/article/details/104216188