三数之和[中等][双指针\排序]

148 阅读2分钟

题目

image.png

思路

首先将数组进行从小到大的排序,然后对于小于0的数字num,设置-num是target,然后设置num后面的位置是left,数组最右边是right,将left和right向中间移动遍历,当nums[left] + nums[right] == target的时候,就将这三个数字加入到答案里面。

注意点:

  1. 如果num大于0的话,可以直接退出循环。(因为后面的数字肯定也大于0,没有办法做到三个数字相加为0)
  2. 如果当前的num和遍历到的上一个num相等,那么这次不参与计算。(遵循原则:不包含重复的三元组)
  3. 如果计算得到和为0,那么将当前的数字和上一个答案中的数字进行比较,如果相等,那么不加入答案。(遵循原则:不包含重复的三元组)
  4. 在nums[left] + nums[right] 和 target进行比较的时候,如果是 < target, 那么将left右移,如果是 > target,那么将right左移。

分析时间复杂度: 首先是排序,时间复杂度nlogn;然后对于target的访问是对数组遍历一次,然后在每次找left+right==target的过程中,都会进行一次遍历。因此最终时间复杂度是O(n^2)。

代码

var findTarget = (ans, num, target, index)=>{
    let begin = index+1, end = num.length-1;
    while(begin<end){
        if(num[begin]+num[end]=== target){
            let tmp = ans.length>0? ans[ans.length-1] : null;
            if(tmp == null || -target !== tmp[0] || num[begin] !== tmp[1]){
                ans.push([-target, num[begin], num[end]]);
            }
            begin++;
            end--;
        }else if(num[begin]+num[end]>target){
            end--;
        }else{
            begin++;
        }
    }
}
/**
 * @param {number[]} nums
 * @return {number[][]}
 */
var threeSum = function(nums) {
    // 排序,双指针,转化为两数相加=target的问题
    // 时间复杂度:排序Ologn,遍历数字是n个,找到target是n,因此是n方,O(n^2)
    nums.sort((a, b)=>(a - b))
    let ans = new Array();
    for(let i=0;i<nums.length;i++){
        if(nums[i]>0){
            break;
        }else if(i==0 || nums[i-1] !== nums[i]){
            findTarget(ans, nums, -nums[i], i);
        }
    }
    return ans;
};