三数之和问题的解决方案

5 阅读2分钟

三数之和问题的解决方案

在编程中,三数之和问题是一个经典的数组操作问题。本文将详细介绍如何高效解决三数之和为目标值的问题。

问题分析

三数之和问题要求在一个数组中找出所有三个数的组合,使得它们的和等于目标值。如果采用暴力解法,需要三层循环遍历所有可能的组合,时间复杂度为 O (n³),效率较低。

优化思路

通过分析,我们可以采用以下优化策略:

  1. 先排序:对数组进行排序(时间复杂度为 O (nlogn)),这样做有几个好处:

    • 方便跳过重复的数字,避免出现重复的结果
    • 可以使用双指针技巧来优化查找过程
  2. 固定一个数字 + 双指针

    • 固定一个数字作为三数中的第一个数
    • 使用左指针指向固定数字的下一个元素
    • 使用右指针指向数组的最后一个元素
    • 根据三数之和与目标值的比较,移动指针寻找合适的组合

代码实现

下面是基于上述思路的代码实现:

function threeSum(nums){
    // 先排序,采用升序排序
    nums.sort((a,b) => a-b);
    const res = [];
    
    // 固定一个数字,循环遍历
    for(let i=0;i<nums.length-2;i++){
        // 跳过重复的起点,避免重复结果
        if(i > 0 && nums[i] === nums[i-1]){
            continue;
        }
        
        let left = i + 1;
        let right = nums.length - 1;
        
        // 双指针查找
        while(left < right){
            const sum = nums[i] + nums[left] + nums[right];
            
            if(sum === 0){
                // 找到符合条件的组合,加入结果集
                res.push([nums[i], nums[left], nums[right]]);
                
                // 继续寻找其他可能的组合
                left++;
                right--;
                
                // 跳过重复的左指针元素
                while(left < right && nums[left] === nums[left-1]){
                    left++;
                }
                
                // 跳过重复的右指针元素
                while(left < right && nums[right] === nums[right+1]){
                    right--;
                }
            }else if(sum < 0){
                // 和小于目标值,左指针右移增大总和
                left++;
            }else{
                // 和大于目标值,右指针左移减小总和
                right--;
            }
        }
    }
    
    return res;
}

排序的细节

在 JavaScript 中,数组的 sort 方法默认是按字符串 Unicode 码点排序的,所以我们需要传入一个比较函数来实现数字的正确排序:

// 升序排序实现
nums.sort((a,b) => {
    return a - b;
});

当 a - b < 0 时,a 会被排在 b 前面,实现了从小到大的排序。

算法复杂度分析

  • 时间复杂度:排序过程为 O (nlogn),外层循环为 O (n),内层双指针遍历为 O (n),总体时间复杂度为 O (n²)
  • 空间复杂度:O (1)(不考虑存储结果的空间)