Description

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.

Notice that the solution set must not contain duplicate triplets.

Example 1:

  • Input: nums = [-1,0,1,2,-1,-4]
  • Output: [[-1,-1,2],[-1,0,1]]
  • Explanation:
    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.
    The distinct triplets are [-1,0,1] and [-1,-1,2].
    Notice that the order of the output and the order of the triplets does not matter.

Example 2:

  • Input: nums = [0,1,1]
  • Output: []
  • Explanation: The only possible triplet does not sum up to 0.

Example 3:

  • Input: nums = [0,0,0]
  • Output: [[0,0,0]]
  • Explanation: The only possible triplet sums up to 0.

Constraints:

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

💡 Hint 1:
So, we essentially need to find three numbers x, y, and z such that they add up to the given value. If we fix one of the numbers say x, we are left with the two-sum problem at hand!

💡 Hint 2:
For the two-sum problem, if we fix one of the numbers, say x, we have to scan the entire array to find the next number y, which is value - x where value is the input parameter. Can we change our array somehow so that this search becomes faster?

💡 Hint 3:
The second train of thought for two-sum is, without changing the array, can we use additional space somehow? Like maybe a hash map to speed up the search?

Submitted Code

class Solution:
    def threeSum(self, nums: list[int]) -> list[list[int]]:
        nums.sort()     # 오름차순 정렬
        n = len(nums)
        result = []

        for mark in range(n):
            if mark > 0 and nums[mark] == nums[mark-1]:                   # mark 숫자 중복방지
                continue
            left, right = mark+1, n-1
            while left < right:
                total = nums[mark] + nums[left] + nums[right]
                if total < 0:
                    left += 1
                elif total > 0:
                    right -= 1
                else:
                    result.append([nums[mark], nums[left], nums[right]])
                    left += 1
                    right -= 1
                    while left < right and nums[left] == nums[left-1]:    # left 숫자 중복방지
                        left += 1
                    while left < right and nums[right] == nums[right+1]:  # right 숫자 중복방지
                        right -= 1

        return result

Runtime: 633 ms | Beats 56.99%
Memory: 22.30 MB | Beats 74.32%

숫자를 순서대로 하나씩 마크하고, 나머지 두 숫자는 left, right 포인트 두개로 확인하는 방법이 공간을 절약할 수 있어서 효율적이다. 중복된 조합을 피하기 위해서 nums를 오름차순으로 정렬하고 mark < left < right 구조를 유지했다.

Other Solutions

1st

def threeSum(self, nums: List[int]) -> List[List[int]]:

	res = set()

	#1. Split nums into three lists: negative numbers, positive numbers, and zeros
	n, p, z = [], [], []
	for num in nums:
		if num > 0:
			p.append(num)
		elif num < 0: 
			n.append(num)
		else:
			z.append(num)

	#2. Create a separate set for negatives and positives for O(1) look-up times
	N, P = set(n), set(p)

	#3. If there is at least 1 zero in the list, add all cases where -num exists in N and num exists in P
	#   i.e. (-3, 0, 3) = 0
	if z:
		for num in P:
			if -1*num in N:
				res.add((-1*num, 0, num))

	#3. If there are at least 3 zeros in the list then also include (0, 0, 0) = 0
	if len(z) >= 3:
		res.add((0,0,0))

	#4. For all pairs of negative numbers (-3, -1), check to see if their complement (4)
	#   exists in the positive number set
	for i in range(len(n)):
		for j in range(i+1,len(n)):
			target = -1*(n[i]+n[j])
			if target in P:
				res.add(tuple(sorted([n[i],n[j],target])))

	#5. For all pairs of positive numbers (1, 1), check to see if their complement (-2)
	#   exists in the negative number set
	for i in range(len(p)):
		for j in range(i+1,len(p)):
			target = -1*(p[i]+p[j])
			if target in N:
				res.add(tuple(sorted([p[i],p[j],target])))

	return res

time complexity: 𝑂(𝑛2)
space complexity: 𝑂(𝑛)

이 방법은 조합 결과를 저장할 때 정렬을 하고 res는 set 타입이기 때문에 중복된 조합을 거를 수 있다.

Leave a comment