02. [Python+Golang+PHP]三数之和,多种语言实现最优解demo

一、问题描述:三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != kj != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。

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

示例 1:

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

nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。

nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。

nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。

不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。

注意,输出的顺序和三元组的顺序并不重要。

示例 2:

输入: nums = [0,1,1]
输出: []
**解释:**唯一可能的三元组和不为 0 。

示例 3:

输入: nums = [0,0,0]
输出: [[0,0,0]]
**解释:**唯一可能的三元组和为 0 。

提示:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <=

二、实现方案

方案一:双指针法

方法思路:

  1. 排序数组:首先将数组排序,以便于后续处理重复元素和使用双指针法。
  2. 固定第一个元素:遍历数组中的每个元素作为三元组的第一个元素。
  3. 双指针查找:对于每个固定的第一个元素,使用双指针在剩余部分中查找另外两个元素,使得三数之和为 0。
  4. 跳过重复元素:在遍历和查找过程中,跳过重复的元素以避免生成重复的三元组。

复杂度:

  • 时间复杂度为 O(n²),
  • 空间复杂度为 O(1)(不考虑排序的空间),是解决此问题的最优方案。

代码实现:

  • Python
python 复制代码
def threeSum(nums):
    nums.sort()
    res = []
    n = len(nums)
    for i in range(n - 2):
        # 跳过重复的第一个元素
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        left, right = i + 1, n - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total < 0:
                left += 1
            elif total > 0:
                right -= 1
            else:
                res.append([nums[i], nums[left], nums[right]])
                # 跳过左侧重复元素
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                # 跳过右侧重复元素
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1
                # 移动指针以寻找下一个可能的三元组
                left += 1
                right -= 1
    return res
  • php:
php 复制代码
function threeSum($nums) {
    sort($nums); // 将数组排序
    $result = array();
    $n = count($nums);

    for ($i = 0; $i < $n - 2; $i++) {
        // 跳过重复的起始元素
        if ($i > 0 && $nums[$i] == $nums[$i - 1]) {
            continue;
        }

        $left = $i + 1;
        $right = $n - 1;
        $target = -$nums[$i]; // 转化为两数之和问题:nums[left] + nums[right] = target

        while ($left < $right) {
            $sum = $nums[$left] + $nums[$right];

            if ($sum == $target) {
                // 找到有效组合,加入结果
                array_push($result, array($nums[$i], $nums[$left], $nums[$right]));
                
                // 跳过左侧重复元素
                while ($left < $right && $nums[$left] == $nums[$left + 1]) {
                    $left++;
                }
                // 跳过右侧重复元素
                while ($left < $right && $nums[$right] == $nums[$right - 1]) {
                    $right--;
                }
                // 移动指针寻找下一个可能
                $left++;
                $right--;
            } elseif ($sum < $target) {
                $left++; // 和太小,左指针右移
            } else {
                $right--; // 和太大,右指针左移
            }
        }
    }

    return $result;
}
  • golang:
Go 复制代码
package main

import (
    "fmt"
    "sort"
)

func threeSum(nums []int) [][]int {
    sort.Ints(nums) // 先排序
    var result [][]int
    n := len(nums)

    for i := 0; i < n-2; i++ {
        // 跳过重复的第一个元素
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }

        left, right := i+1, n-1
        target := -nums[i] // 转化为两数之和问题

        for left < right {
            sum := nums[left] + nums[right]
            if sum == target {
                // 找到有效组合
                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--
            } else if sum < target {
                left++ // 和太小,左指针右移
            } else {
                right-- // 和太大,右指针左移
            }
        }
    }
    return result
}

## 测试实例
func main() {
    // 示例 1
    nums1 := []int{-1, 0, 1, 2, -1, -4}
    fmt.Println(threeSum(nums1)) // 输出:[[-1 -1 2] [-1 0 1]]

    // 示例 2
    nums2 := []int{0, 1, 1}
    fmt.Println(threeSum(nums2)) // 输出:[]

    // 示例 3
    nums3 := []int{0, 0, 0}
    fmt.Println(threeSum(nums3)) // 输出:[[0 0 0]]
}

方案二:暴力法(三重循环)

复杂度:

  • 时间复杂度:O(n³),适用于极小的数据量,但在题目约束下会超时。

代码实现:

  • Python:
python 复制代码
def threeSumBruteForce(nums):
    res = []
    nums.sort()
    n = len(nums)
    for i in range(n-2):
        if i > 0 and nums[i] == nums[i-1]:
            continue
        for j in range(i+1, n-1):
            if j > i+1 and nums[j] == nums[j-1]:
                continue
            for k in range(j+1, n):
                if k > j+1 and nums[k] == nums[k-1]:
                    continue
                if nums[i] + nums[j] + nums[k] == 0:
                    res.append([nums[i], nums[j], nums[k]])
    return res
  • PHP:
php 复制代码
function threeSumBruteForce($nums) {
    sort($nums);
    $result = array();
    $n = count($nums);
    for ($i = 0; $i < $n - 2; $i++) {
        if ($i > 0 && $nums[$i] == $nums[$i-1]) continue;
        for ($j = $i+1; $j < $n - 1; $j++) {
            if ($j > $i+1 && $nums[$j] == $nums[$j-1]) continue;
            for ($k = $j+1; $k < $n; $k++) {
                if ($k > $j+1 && $nums[$k] == $nums[$k-1]) continue;
                if ($nums[$i] + $nums[$j] + $nums[$k] == 0) {
                    array_push($result, [$nums[$i], $nums[$j], $nums[$k]]);
                }
            }
        }
    }
    return $result;
}
  • Golang:
Go 复制代码
func threeSumBruteForce(nums []int) [][]int {
    sort.Ints(nums)
    var result [][]int
    n := len(nums)
    for i := 0; i < n-2; i++ {
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        for j := i + 1; j < n-1; j++ {
            if j > i+1 && nums[j] == nums[j-1] {
                continue
            }
            for k := j + 1; k < n; k++ {
                if k > j+1 && nums[k] == nums[k-1] {
                    continue
                }
                if nums[i]+nums[j]+nums[k] == 0 {
                    result = append(result, []int{nums[i], nums[j], nums[k]})
                }
            }
        }
    }
    return result
}

方案三:哈希表法

复杂度:

  • 时间复杂度:O(n²),但哈希表操作常数较大,实际效率可能不如双指针法。

代码示例

  • python:
python 复制代码
def threeSumHash(nums):
    res = []
    nums.sort()
    n = len(nums)
    for i in range(n - 2):
        if i > 0 and nums[i] == nums[i - 1]:
            continue
        seen = set()
        target = -nums[i]
        j = i + 1
        while j < n:
            complement = target - nums[j]
            if complement in seen:
                res.append([nums[i], complement, nums[j]])
                while j + 1 < n and nums[j] == nums[j + 1]:
                    j += 1
            seen.add(nums[j])
            j += 1
    return res
  • php:
php 复制代码
function threeSumHash($nums) {
    sort($nums);
    $result = array();
    $n = count($nums);
    for ($i = 0; $i < $n - 2; $i++) {
        if ($i > 0 && $nums[$i] == $nums[$i-1]) continue;
        $seen = array();
        $target = -$nums[$i];
        for ($j = $i + 1; $j < $n; $j++) {
            $complement = $target - $nums[$j];
            if (in_array($complement, $seen)) {
                array_push($result, [$nums[$i], $complement, $nums[$j]]);
                while ($j + 1 < $n && $nums[$j] == $nums[$j+1]) $j++;
            }
            array_push($seen, $nums[$j]);
        }
    }
    return $result;
}
  • Golang:
Go 复制代码
func threeSumHash(nums []int) [][]int {
    sort.Ints(nums)
    var result [][]int
    n := len(nums)
    for i := 0; i < n-2; i++ {
        if i > 0 && nums[i] == nums[i-1] {
            continue
        }
        seen := make(map[int]bool)
        target := -nums[i]
        for j := i + 1; j < n; j++ {
            complement := target - nums[j]
            if seen[complement] {
                result = append(result, []int{nums[i], complement, nums[j]})
                // 跳过重复的j
                for j+1 < n && nums[j] == nums[j+1] {
                    j++
                }
            }
            seen[nums[j]] = true
        }
    }
    return result
}

三、效率对比

  • 双指针法:最优解,时间复杂度 O(n²),空间复杂度 O(1)(排序的辅助空间可忽略),适用于大规模数据。
  • 哈希表法:同样时间复杂度 O(n²),但实际运行效率较低,且处理重复元素逻辑复杂。
  • 暴力法:仅适用于极小数据量,实际不可行。
相关推荐
liuyang-neu40 分钟前
力扣 155.最小栈
java·算法·leetcode
心软且酷丶1 小时前
leetcode:2160. 拆分数位后四位数字的最小和(python3解法,数学相关算法题)
python·算法·leetcode
Musennn2 小时前
leetcode98.验证二叉搜索树:递归法中序遍历的递增性验证之道
java·数据结构·算法·leetcode
WLKQ2 小时前
【力扣】关于链表索引
java·leetcode·链表
reduceanxiety2 小时前
机试 | vector/array Minimum Glutton C++
数据结构·c++·算法
盛夏绽放2 小时前
Python常用高阶函数全面解析:通俗易懂的指南
前端·windows·python
2301_794461573 小时前
力扣-最大连续一的个数
数据结构·算法·leetcode
MonKingWD3 小时前
【redis原理篇】底层数据结构
数据结构·数据库·redis
仟濹3 小时前
Python - 文件部分
python
一点.点3 小时前
李沐动手深度学习(pycharm中运行笔记)——10.多层感知机+从零实现+简介实现
人工智能·笔记·python·深度学习·pycharm