每日一题(三数之和) | 刷题打卡

226 阅读2分钟

掘金团队号上线,助你 Offer 临门! 点击 查看详情

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

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

二、思路分析
这道题主要考察了双指针的使用,且需要考虑到去重的问题,去重的常规思想是使用散列函数,针对数组,我们可以使用排序+jump的方式做到去重的实现,另外考虑边界问题,数组本身大小和要求的结果。 一开始的思路肯定是三个for循环,暴力求解,每层循环都要考虑到和上次的元素是否相等,如果相等,就跳过这次,这样的时间复杂度为O(N^3),后续考虑到优化问题,我们可以在第二层和第三层做处理。

  • 先固定住第一层,因为数组已经从小到大排序故遍历第一层的时候如果元素的值大于0,那么后续的元素直接可以不用考虑了,另外,第一层的元素也必须满足不和上一次的元素重复
  • 接下来两个指针,一个是从大于第一层的第一个元素开始,从前往后遍历,另一个是从最后一个元素往前遍历,且这两个指针不能相交,因为不能使用同一个元素两次,这样去枚举所有结果,因为这两个指针的遍历可以同步进行(左指针不动,右指针移动N-左指针个长度)在这一层的时间复杂度均摊下来为O(N),因此整个的时间复杂度为O(N^2),算上排序的时间复杂度O(nlogn),在渐进条件下,时间复杂度还是趋向于O(N^2)

三、AC代码

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        if(nums == null || nums.length <= 2){
            return new ArrayList();
        }
        int n = nums.length;
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList();
        for(int i = 0 ;i<n;i++){
            int l = i+1;
            int r = n-1;
            int target = -nums[i];
            if(nums[i] > 0){
                break;
            }
            if(i>0 && nums[i] == nums[i-1]){
                continue;
            }
            while(l < r){
                if(nums[l]+nums[r] == target){
                    //将寻找到的结果放入结果数组
                    List<Integer> temp = new ArrayList();
                    temp.add(nums[l]);
                    temp.add(nums[i]);
                    temp.add(nums[r]);
                    result.add(temp);
                    //指针指向下一组的第二和第三个元素
                    l++;
                    r--;
                    //不能和这一组的第二,第三个元素相同,循环直到找出不同的元素为止,然后开始下一组的寻找
                    while(l<r && nums[l]==nums[l-1]){
                        l++;
                    }
                    while(l<r && nums[r]==nums[r+1]){
                        r--;
                    }
                }else if(nums[l]+nums[r] < target){
                //小于0,左指针增加,总数值增大
                    l++;
                }else{
                //大于0,右指针减小,总数值减小
                    r--;
                }
            }
        }
        return result;
    }
}

四、总结
遇到需要枚举数组中的元素时候:如果需要枚举的第一个元素是递增的,第二个元素是递减的,我们就可以使用双指针的方式,枚举出所有需要的组合。