LeetCode中三数之和

358 阅读4分钟

LeetCode中数组中三数之和

LeetCode中的数组题目以实际问题背景和算法优化为主要考察方向,经常涉及到常见算法思想。

三数之和(3Sum)问题是数组中常见的问题之一。给定一个整数数组 nums,找出其中三个数满足它们的和为 0。

示例:

输入:nums = [-1,0,1,2,-1,-4] 输出:[[-1,-1,2],[-1,0,1]]

在LeetCode中,我们可以在题库中搜索到三数之和问题,这里以题号15为例。在阅读问题描述后,我们需要考虑问题的解决方式。对于三数之和,我们可以采用暴力破解或使用特定的算法来解决。在算法实现中,我们需要注意优化算法以提高效率。

步骤1: 算法的初步风格检查

首先,我们需要检查算法的初步风格。我们可以先按照伪代码实现算法。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        int len = nums.length;
        if(len < 3) return ans;
        Arrays.sort(nums); // 排序
        for (int i = 0; i < len ; i++) {
            if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
            if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
            int L = i+1;
            int R = len-1;
            while(L < R){
                int sum = nums[i] + nums[L] + nums[R];
                if(sum == 0){
                    ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
                    while (L<R && nums[L] == nums[L+1]) L++; // 去重
                    while (L<R && nums[R] == nums[R-1]) R--; // 去重
                    L++;
                    R--;
                }
                else if (sum < 0) L++;
                else if (sum > 0) R--;
            }
        }
        return ans;
    }
}

以上是参考官方文档给出的Solution ,其通过先排序,然后枚举第一个数,接着在剩下的数字中使用双指针枚举两个数,时间复杂度为 O(N^2)。

接下来我们对以上代码进行解析,找出其中使用到的算法优化。

步骤2: 算法优化的相关知识点

  1. 排序

对于三数之和问题,排序是其中一个必经之路。为什么需要排序呢?先排序可以使整个数组变得有序,并使得枚举的过程前后循序一致。

在Java中通过sort()函数可以轻松地对数组进行排序。

    Arrays.sort(nums);
  1. 双指针

双指针算法在LeetCode中也十分常见。对于三数之和的问题,我们在排序后,两层遍历枚举第一层和第二层,第三层通过左右指针进行计算。这其中的双指针算法提供了极大的帮助。

接下来我们看一下在三数之和问题中左右指针如何运用:

            int L = i+1;
            int R = len-1;
            while(L < R){
                int sum = nums[i] + nums[L] + nums[R];
                if(sum == 0){
                    ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
                    while (L<R && nums[L] == nums[L+1]) L++; // 去重
                    while (L<R && nums[R] == nums[R-1]) R--; // 去重
                    L++;
                    R--;
                }
                else if (sum < 0) L++;
                else if (sum > 0) R--;
            }
  1. 去重

对于三数之和的问题,我们需要注意去重。在对数组进行排序后,可以判断两个数字是否相同。

            if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
  1. 数组实现及索引获取

在Java中,数组是最常见的数据类型之一。我们可以通过固定的索引值直接进行数组调用,获取数组的某一项。 例如:

        nums[2]
  1. List

List是Java中常用的容器类,他的主要用途是动态存放集合中的元素。

List中有六种实现方式,每种方式都有特定的优劣,常用的方式有 ArrayList 和 LinkedList。

在大多数场景下,ArrayList 是最常用的 List 实现。它使用动态数组来存储所有的元素,因此它具有通过索引访问元素的能力,并在列表末尾添加和删除元素的良好性能。

在上面的Solution中,我们就使用了ArrayList来存储结果的元素。在Java中实现List可以使用以下代码:

        List<List<Integer>> ans = new ArrayList<>();

在这里,我们调用Java中ArrayList的add()方法,将符合条件的三元组添加到ans中。

  1. 时间复杂度与空间复杂度分析

算法的时间复杂度和空间复杂度是评价算法性能的两个重要指标,需要在算法实现过程中进行分析。 对于三数之和的问题,我们使用了双指针和去重等算法优化,时间复杂度为O(N^2),空间复杂度为O(N),其中N指数组的长度。

步骤3: 算法实现过程

在了解问题及其算法优化之后,我们可以开始着手实现算法解决方案。在编写代码时,需要注意变量名的规范以及代码的清晰易懂。

下面是基于以上算法思路的三数之和问题Java解决方案代码示例:

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList<>();
        int len = nums.length;
        if(len < 3) return ans;
        Arrays.sort(nums); // 排序
        for (int i = 0; i < len ; i++) {
            if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
            if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
            int L = i+1;
            int R = len-1;
            while(L < R){
                int sum = nums[i] + nums[L] + nums[R];
                if(sum == 0){
                    ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
                    while (L<R && nums[L] == nums[L+1]) L++; // 去重
                    while (L<R && nums[R] == nums[R-1]) R--; // 去重
                    L++;
                    R--;
                }
                else if (sum < 0) L++;
                else if (sum > 0) R--;
            }
        }
        return ans;
    }
}

通过以上代码我们可以看出,在 Java 语言中实现三数之和问题,既有简单的语言特性,又有算法优化的实现,是一个综合能力较高,需要积累学习和实践的算法问题。

总结

三数之和问题是一个常见的算法优化问题,在LeetCode和其他算法题库中经常出现。通过学习和练习,我们可以掌握以下技能:

  1. 排序算法对于大多数算法问题都是基础且必须掌握的算法思想,可在算法实现过程中体现出排序算法在时间复杂度上的优势。
  2. 双指针算法在算法实现中有很好的实践价值,可以很好的提高思路的灵活性,有效优化算法时间复杂度。
  3. 算法去重在算法实现中非常重要,可以有效节省空间复杂度和避免重复计算,需要细心分析和实践。
  4. 计算数组元素时间复杂度和空间复杂度是评价算法性能的重要指标,需要在实现过程中做到全方位的考虑。