[leetcode-15]3 Sum

108 阅读1分钟

题目

CleanShot 2022-11-07 at 20.56.23@2x.png

分析

这道题用一句话来归纳就是:不重复地找出所有和为0的三个数。

要想和为0, 则3个数里必须有正数也有负数,从这里可以考虑是否先将数组进行排序,这样正数和负数会被分别放到一起。

另一方面,如果只是找两个数之和,我们可以进行一个双重遍历,找个数之和,我们固然可以进行高时间复杂度的三重遍历,但是如果在第二个循环中用pointer(指针),我们便可以避免使用三重循环。

解决方法

const threeSum = function(nums) {
    //先将数组进行排序
    nums.sort((a, b) => a - b);

    // 如果数组全是正数,或全是负数,则返回空数组;
    if (nums[0] > 0 || nums[nums.length - 1] < 0) {
        return [];
    }

    const res = [];
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] > 0) { //负数和0部分已经遍历完后,后面的正数就不用再遍历了。
            break;
        }

        if (i > 0 && nums[i] == nums[i - 1]) { //跳过重复元素
            continue;
        }

        let sum = 0;                    // 定义sum和两个指针
        let left = i + 1;
        let right = nums.length - 1;

        while (left < right) {
            sum = nums[i] + nums[left] + nums[right];          //计算当前sum
            if (sum > 0) {                                     //如果sum>0,就将右指针左移
               right--;
            } 
            else if (sum < 0) {                             //如果sum<0,就将左指针右移
               left++;
            } 
            else {
                res.push([nums[i], nums[left], nums[right]]); 
                right = shiftRightPointer(nums, left, right);  //往中间移动左右指针,跳过重复元素,避免push重复数据。
                left= shiftLeftPointer(nums, left, right);
            }
        }
        
    }
    return res;
};

// ********** 
function shiftRightPointer(nums, left, right) {  // 将right指针向左移,跳过重复元素 
    let last_right_item = nums[right];       
    do {
        right--;
    }
    while (left < right && nums[right] == last_right_item)
    return right;
}

function shiftLeftPointer(nums, left, right) { // 将left指针向右移,跳过重复元素 
    let last_left_item = nums[left];
    do {
        left++;
    }
    while (left < right && nums[left] == last_left_item)
    return left;
}