LeetCode#15 | Three Sum 三数之和

Posted 呦呦鹿鸣

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode#15 | Three Sum 三数之和相关的知识,希望对你有一定的参考价值。

一、题目

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

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

示例:

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

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

二、题解

思路1:

之前有道算法题,是求两数之和,所以想法就是先遍历数组,固定当前元素,并求出当前元素的相反数,即剩下两个数的和sum;然后转化成 twoSum;最后去重。

本地跑是没问题的,但是,在力扣上提交代码,等待系统判分后,执行结果是超出时间限制——系统给的输入是好大一段数组,里面得有几百个数字吧?!??所以下面的代码适合小数组。

function threeSum($nums) {
    $result = [];
    $len = count($nums);
    for ($i = 0; $i < $len; $i++) {
        $a = $nums[$i];
        $sum = ($a > 0) ? 0 - $a : abs($a);
        
        $searched = [];
        for ($j = 0; $j < $len; $j++) {
            if ($j == $i) {
                continue;
            }
            if (!in_array($sum - $nums[$j], $searched)) {
                $searched[] = $nums[$j];
            } else {
                $tmp = [$a, $nums[$j], $sum - $nums[$j]];
                sort($tmp);
                if (in_array($tmp, $result)) {
                    continue;
                }
                $result[] = $tmp;
            }
        }
    }
    return $result;
}
思路2:双指针法

首先剔除一些特殊情况:①数组元素个数小于3;②排序后最小值大于0;③排序后最大值小于0;
对数组进行排序;
从第一个元素开始,遍历至倒数第三个元素;
每一次遍历中,使用双指针处理该元素右侧的数组,问题基本转化为 twoSum:
①用两个变量 low 和 high 指向数组的开头和结尾;
②因为已经进行过排序,如果nums[low]+nums[high] < sum,则说明low指向的数太小,需要往后移动;
③反之,则是high指向的数太大,需要前移;
④当两者相等,未避免得到重复的三元组集合,遇到重复的数字就跳过。

function threeSum($nums) {
    $len = count($nums);
    if ($len < 3) {
        return [];
    }

    sort($nums);
    if ($nums[0] > 0 || end($nums) < 0) {
        return [];
    }

    $res = [];
    for ($i = 0; $i < $len; $i++) {
        // 如果 i 与 i-1 值相同,说明上一次比较过了
        if ($nums[$i] == $nums[$i-1]) {
            continue;
        }
        // 要对比的值
        $target = 0 - $nums[$i];

        $low = $i + 1;
        $high = $len - 1;
        while ($low < $high) {
            if ($nums[$low] + $nums[$high] == $target) {
                $res[] = [$nums[$i], $nums[$low], $nums[$high]];
                // 因为数组已经排好序,且要求返回的组合不能重复
                // 如果nums[low] == nums[low+1] 说明两个数重复,数据组合会重合。将 low 向后偏移一位
                while ($low < $high && $nums[$low] == $nums[$low + 1]) {
                    $low ++;
                }
                // 如果nums[high] == nums[high-1] 说明两个数重复,数据组合会重合。将 high 向前偏移一位
                while ($low < $high && $nums[$high] == $nums[$high - 1]) {
                    $high --;
                }
                // low 与 high 这对组合已经使用过,因此需要继续偏移
                $low ++; 
                $high --;
            } else if ($nums[$low] + $nums[$high] < $target) {
                $low ++;
            } else {
                $high --;
            }
        }
    }

    return $res;
}

以上是关于LeetCode#15 | Three Sum 三数之和的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 1013. Partition Array Into Three Parts With Equal Sum

leetcode-3Sum-15

Leetcode15 3Sum

LeetCode15题: 寻找三数和,附完整代码

(Easy) Partition Array Into Three Parts With Equal Sum - LeetCode

Leetcode 1262. Greatest Sum Divisible by Three