0015-leetcode算法实现之三数之和-3sum-python&golang实现

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

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

示例 1:

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

输入:nums = []
输出:[]
示例 3:

输入:nums = [0]
输出:[]

提示:

0 <= nums.length <= 3000
-105 <= nums[i] <= 105

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum

python

# 三数之和为0

class Solution:
    def threeSum(self, nums: [int]) -> [int]:
        """
        先排序,再用双指针,排序O(nlogn),寻找二三个数O(n),寻找一二三个数O(n^2),所以时间O(n),空间O(logn)
        思路:整形数组,先排序,利于后面寻找三个数,每轮搜索,固定第一个数,第二个数右移,第三个数左移,比较和与target
        算法流程:
        1.排序数组,初始化返回数组ans []
        2.遍历数组,检查当前的first指向元素是否大于0,成立则break,不可能有a+b+c=0成立,检查first是大于0即第二个数的first开始, 且与前一个元素值相同,继续下一个first
        3.每次遍历寻找第一个数时,初始化third为最后一个元素,第一个元素值的相反数即为搜索本次二三个数和的目标值target
        4.再次遍历数组,从first+1开始,检查second和second-1对应元素值是否相等,等则continue,继续寻找second
        5.当second<third且和大于target时,由于排序,此时只有减小和才可能等于target,即third--, 重复移动third寻找合适的third,当second=third或和<=target时,break当前的while循环
        6.上步break, 检查second==third, 成立则break second third,应该更新first,继续2 3 4 5 6
        7.5步的while循环仅second=third或和等于小于target时break, 如果和等target则将此三个数append进ans, 如果不等target,其实就是和小于target, 即继续更新second, 重复4 5 6 7步
        8.重复2-7,直到break或遍历完数组,返回ans
        :param nums:
        :return:
        """
        nums.sort() # 为便于后面寻找,先排序
        n = len(nums)
        ans = [] # 初始化返回数组
        for first in range(n):
            if nums[first] > 0: # 由于数组已经排序,只要第一个数大于0,后面的数肯定都大于0,不可能出现a+b+c=0,直接break,就不用再去寻找后面的可能性
                break
            if first > 0 and nums[first] == nums[first-1]: # 第一个数在遍历到第二及以后的数时,检查是否与前一个数重复,是则continue,否则继续寻找二三个数
                continue
            third = n -1 # 每次更新第一个数时,third指向最后一个数
            target = -nums[first] # target对第一个数取反,后面的二三个数,只需要考虑其和与target的比较,每次更新第一个数的时候,更新target
            for second in range(first+1, n):
                # 第二个数与上次遍历相同,继续遍历,寻找第二个数
                if second > first + 1 and nums[second] == nums[second-1]:
                    continue
                # 左指针在右指针左侧,且和大于target
                while second < third and nums[second] + nums[third] > target: # 由于是排序数组,大概率应该是其和小于target的,只有当左指针在右指针左边,且和大于target,左移右指针third--,更新third
                    third -= 1
                # 左右指针重合或左右指针数和小于等于target
                if second == third: # 大概率是和小于target,此时应该更新second,和才会变大,检查左右指针是否相等,如果等,即二三数为同一个数,必须break
                    break
                if nums[second] + nums[third] == target: # 检查二三数和是否等于target, 成立则append进ans,继续更新second
                    ans.append([nums[first], nums[second], nums[third]])
        return ans




if __name__ == "__main__":
    nums = [2,6,7,-8,3,4,2,-13,3,-5]
    nums1 = []
    nums2 = [0]
    nums3 = [1,2]
    test = Solution()
    print(test.threeSum(nums)) # [[-13, 6, 7], [-8, 2, 6], [-5, 2, 3]]
    print(test.threeSum(nums1)) # []
    print(test.threeSum(nums2)) # []
    print(test.threeSum(nums3)) # []

golang

package main

import (
	"fmt"
	"sort"
)

func main() {
	nums := []int{2, 6, 7, -8, 3, 4, 2, -13, 3, -5}
	fmt.Println(threeSum(nums))
}

// 排序+双指针
func threeSum(nums []int) [][]int {
	sort.Ints(nums)
	n := len(nums)
	ans := [][]int{}
	var first int

	for first = 0; first < n; first++ {
		if nums[first] > 0 {
			break
		}
		if first > 0 && nums[first] == nums[first-1] {
			continue
		}
		third := n - 1
		target := -nums[first]
		for second := first + 1; second < n-1; second++ {
			if second > first+1 && nums[second] == nums[second-1] {
				continue
			}
			for second < third && nums[second]+nums[third] > target {
				third--
			}
			if second == third {
				break
			}
			if nums[second]+nums[third] == target {
				ans = append(ans, []int{nums[first], nums[second], nums[third]})
			}
		}
	}
	return ans
}

posted on 2021-10-27 23:19  进击的davis  阅读(64)  评论(0编辑  收藏  举报

导航