三数之和

题目要求

给定一个整数数组 nums,要求找出所有不同的三元组 [nums[i], nums[j], nums[k]],使得 ijk 互不相同,并且满足 nums[i] + nums[j] + nums[k] == 0。需要注意的是,返回的三元组列表中不能包含重复的三元组。

解题思路

解决这个问题的一个有效方法是使用排序加双指针的策略。以下是解题的步骤:

  1. 排序数组:首先对数组 nums 进行排序。排序的目的是为了方便后续操作,可以使用双指针技术来避免重复的三元组,并且可以更快地跳过那些不可能组成和为 0 的三元组。

  2. 遍历数组:遍历排序后的数组,对于每个元素 nums[i],设置两个指针,一个指向 i+1(左指针),另一个指向数组的最后一个元素(右指针)。遍历的过程中,如果 i 大于 0 且 nums[i] 等于 nums[i-1],则跳过这个元素以避免重复的三元组。

  3. 双指针查找:在固定了 nums[i] 之后,移动左右指针来寻找两个数,使得它们的和加上 nums[i] 等于 0。如果找到了这样的两个数,就将它们与 nums[i] 一起作为一个三元组添加到结果列表中。添加后,需要继续移动左右指针,并跳过所有重复的值,以防止添加重复的三元组。

  4. 左指针右移:如果当前的三个数的和小于 0,说明需要增加数值,因此将左指针向右移动。

  5. 右指针左移:如果当前的三个数的和大于 0,说明需要减少数值,因此将右指针向左移动。

  6. 重复上述步骤:重复步骤 3 到 5,直到左指针不再小于右指针。

  7. 返回结果:遍历完成后,返回存储三元组的结果列表。

通过以上步骤,可以确保找到所有不重复的三元组,且每个三元组的和为 0。

Golang 版本

package main

import (
	"fmt"
	"sort"
)

func threeSum(nums []int) [][]int {
	sort.Ints(nums)
	result := make([][]int, 0)

	for i := 0; i < len(nums)-2; i++ {
		// 跳过重复的数字
		if i > 0 && nums[i] == nums[i-1] {
			continue
		}

		left, right := i+1, len(nums)-1
		for left < right {
			sum := nums[i] + nums[left] + nums[right]
			switch {
			case sum < 0:
				left++
			case sum > 0:
				right--
			default:
				result = append(result, []int{nums[i], nums[left], nums[right]})
				// 跳过重复的数字
				for left < right && nums[left] == nums[left+1] {
					left++
				}
				for left < right && nums[right] == nums[right-1] {
					right--
				}
				left++
				right--
			}
		}
	}

	return result
}

func main() {
	nums := []int{-1, 0, 1, 2, -1, -4}
	result := threeSum(nums)
	fmt.Println(result) // 输出找到的所有不重复的三元组
}

这段代码定义了一个 threeSum 函数,它接受一个整数切片 nums 作为输入,并返回一个二维切片,其中包含所有和为 0 的不重复三元组。主函数 main 提供了一个示例输入,并打印出 threeSum 函数的输出结果。

C++版本

#include <iostream>
#include <vector>
#include <algorithm>

std::vector<std::vector<int>> threeSum(std::vector<int>& nums) {
    std::vector<std::vector<int>> result;
    std::sort(nums.begin(), nums.end());

    for (int i = 0; i < nums.size(); ++i) {
        if (i > 0 && nums[i] == nums[i - 1]) {
            continue; // 跳过重复值
        }

        int left = i + 1, right = nums.size() - 1;
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            if (sum < 0) {
                ++left;
            } else if (sum > 0) {
                --right;
            } else {
                result.push_back({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;
                ++left;
                --right;
            }
        }
    }

    return result;
}

int main() {
    std::vector<int> nums = {-1, 0, 1, 2, -1, -4};
    std::vector<std::vector<int>> result = threeSum(nums);

    for (const auto& triplet : result) {
        std::cout << '[';
        for (int i = 0; i < triplet.size(); ++i) {
            std::cout << triplet[i];
            if (i < triplet.size() - 1) std::cout << ", ";
        }
        std::cout << ']' << std::endl;
    }

    return 0;
}

这段 C++代码定义了一个 threeSum 函数,它接受一个整数向量 nums 作为输入,并返回一个二维向量,其中包含所有和为 0 的不重复三元组。main 函数提供了一个示例输入,并打印出 threeSum 函数的输出结果。

Python 版本

def three_sum(nums):
    nums.sort()
    result = []
    length = len(nums)
    for i in range(length - 2):
        if i > 0 and nums[i] == nums[i - 1]:
            continue  # Skip the duplicate numbers
        left, right = i + 1, length - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total < 0:
                left += 1
            elif total > 0:
                right -= 1
            else:
                result.append([nums[i], nums[left], nums[right]])
                while left < right and nums[left] == nums[left + 1]:
                    left += 1  # Skip the duplicate numbers
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1  # Skip the duplicate numbers
                left += 1
                right -= 1
    return result

# Example usage:
nums = [-1, 0, 1, 2, -1, -4]
print(three_sum(nums))

这段 Python 代码定义了一个 three_sum 函数,它接受一个整数列表 nums 作为输入,并返回一个列表,其中包含所有和为 0 的不重复三元组。示例用法展示了如何调用这个函数并打印结果。

Java 版本

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList<>();
        for (int i = 0; i < nums.length - 2; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue; // Skip duplicate elements
            }
            int left = i + 1, right = nums.length - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum == 0) {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    while (left < right && nums[left] == nums[left + 1]) {
                        left++; // Skip duplicate elements
                    }
                    while (left < right && nums[right] == nums[right - 1]) {
                        right--; // Skip duplicate elements
                    }
                    left++;
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    right--;
                }
            }
        }
        return result;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = {-1, 0, 1, 2, -1, -4};
        List<List<Integer>> result = solution.threeSum(nums);
        for (List<Integer> triplet : result) {
            System.out.println(triplet);
        }
    }
}

这段 Java 代码定义了一个 Solution 类,其中包含一个 threeSum 方法,它接受一个整数数组 nums 作为输入,并返回一个列表,其中包含所有和为 0 的不重复三元组。main 方法提供了一个示例输入,并打印出 threeSum 方法的输出结果。

Rust 版本

use std::collections::HashSet;

fn three_sum(nums: Vec<i32>) -> Vec<Vec<i32>> {
    let mut nums = nums;
    nums.sort_unstable();
    let mut result = HashSet::new();
    for i in 0..nums.len() {
        if i > 0 && nums[i] == nums[i - 1] {
            continue; // Skip the same result
        }
        let (mut start, mut end) = (i + 1, nums.len() - 1);
        while start < end {
            let sum = nums[i] + nums[start] + nums[end];
            match sum.cmp(&0) {
                std::cmp::Ordering::Equal => {
                    result.insert(vec![nums[i], nums[start], nums[end]]);
                    start += 1;
                    end -= 1;
                    while start < end && nums[start] == nums[start - 1] {
                        start += 1; // Skip the same number
                    }
                    while start < end && nums[end] == nums[end + 1] {
                        end -= 1; // Skip the same number
                    }
                }
                std::cmp::Ordering::Less => start += 1,
                std::cmp::Ordering::Greater => end -= 1,
            }
        }
    }

    result.into_iter().collect()
}

fn main() {
    let nums = vec![-1, 0, 1, 2, -1, -4];
    let triplets = three_sum(nums);
    for triplet in triplets {
        println!("{:?}", triplet);
    }
}

这段 Rust 代码定义了一个 three_sum 函数,它接受一个整数向量 nums 作为输入,并返回一个向量,其中包含所有和为 0 的不重复三元组。main 函数提供了一个示例输入,并打印出 three_sum 函数的输出结果。在这个实现中,使用了 HashSet 来自动去重复的三元组,最后将其转换为一个向量。

总结

上述 Rust 版本的解法采用了以下步骤来找出所有和为 0 的不重复三元组:

  1. 排序: 首先对输入的整数向量 nums 进行排序。这是为了方便后续的双指针遍历,同时也使得相同的数字聚集在一起,便于跳过重复的三元组。

  2. 遍历: 使用外层循环遍历排序后的数组,从第一个元素开始,直到倒数第三个元素。这个元素将作为三元组中的第一个数字。

  3. 跳过重复的元素: 在外层循环中,如果当前元素与前一个元素相同,则跳过当前元素,以避免找到重复的三元组。

  4. 双指针查找: 对于每个外层循环选定的元素,使用两个指针(startend)在剩余数组中寻找两个数,使得这两个数与当前选定的元素之和为 0。start 指针从当前元素的下一个元素开始,end 指针从数组的最后一个元素开始向前移动。

  5. 三数之和判断: 如果三个数的和小于 0,则将 start 指针向右移动;如果和大于 0,则将 end 指针向左移动;如果和等于 0,则将这三个数作为一个三元组添加到结果集中。

  6. 处理指针重复: 在找到一个有效的三元组后,需要移动 startend 指针,同时跳过所有重复的元素,以防止添加重复的三元组到结果集中。

  7. 结果去重: 使用 HashSet 来存储三元组,这样可以自动去除重复的三元组。最后将 HashSet 转换为向量形式返回。

  8. 输出结果: 在 main 函数中,调用 three_sum 函数并打印出返回的所有三元组。

这种解法的时间复杂度主要由排序和双指针遍历决定,排序的时间复杂度为 O(n log n),双指针遍历的时间复杂度为 O(n^2),因此总的时间复杂度为 O(n^2)。空间复杂度主要由结果集的存储决定,最坏情况下为 O(n^2)。