[LeetCode][python3]0015. 3Sum

Start the Journey
N2I -2020.03.21

1. My first try

  1. class Solution:
  2. def threeSum(self, nums: List[int]) -> List[List[int]]:
  3. ans=[]
  4. nums.sort()
  5. #print(nums)
  6. for i in range(len(nums)-2):
  7. if nums[i]>0:
  8. break
  9. if i==0 or nums[i]>nums[i-1]:
  10. j=i+1
  11. k=len(nums)-1
  12. while j<k:
  13. if nums[i]+nums[j]+nums[k] == 0:
  14. ans.append([nums[i],nums[j],nums[k]])
  15. j+=1
  16. k-=1
  17. while j<k and nums[j]==nums[j-1]:
  18. j+=1
  19. while j<k and nums[k]==nums[k+1]:
  20. k-=1
  21. elif -nums[i]>nums[j]+nums[k]:
  22. j+=1
  23. else:
  24. k-=1
  25. #print(ans)
  26. return ans

Explanation:

The classic way to solve this problem. The range between 700 ms to 1100 ms are probably the same method. First you sort the array, then search the array with three pointers.


2. A Recursive Solution

  1. class Solution:
  2. def threeSum(self, nums: List[int]) -> List[List[int]]:
  3. if not nums:
  4. return []
  5. nums = sorted(nums)
  6. res = []
  7. self.kSum(nums, 0, len(nums)-1, 3, 0, [], res)
  8. return res
  9. def kSum(self, nums, left, right, k, target, state, res):
  10. # if the selecting range is not enough
  11. if k < 2 or right - left + 1 < k:
  12. return
  13. if k * nums[left] > target or k * nums[right] < target:
  14. return
  15. if k == 2:
  16. # Solving 2Sum problem
  17. while left < right:
  18. cur = nums[left] + nums[right]
  19. if cur == target:
  20. res.append(state + [nums[left], nums[right]])
  21. left += 1
  22. while left < right and nums[left] == nums[left - 1]:
  23. left += 1
  24. elif cur > target:
  25. right -= 1
  26. else:
  27. left += 1
  28. else:
  29. # k > 2, reduce the degree
  30. for i in range(left, right+1):
  31. if i == left or i > left and nums[i] != nums[i-1]:
  32. self.kSum(nums, i+1, right, k-1, target-nums[i], state+[nums[i]], res)

Explanation:

The solution is a classic recursive way to solve this kind of problems. It reduce its degree in every recursion. Puts any situation into a 2 Sum problem.


3. A Better Solution

  1. class Solution:
  2. def threeSum(self, nums: List[int]) -> List[List[int]]:
  3. ans = set()
  4. count = {}
  5. for i in nums:
  6. if i not in count: count[i] = 1
  7. else: count[i] += 1
  8. nums = sorted(count)
  9. for i, item_i in enumerate(nums):
  10. if item_i < 0:
  11. target = -item_i
  12. left = bisect.bisect_left(nums, target - nums[-1], i + 1)
  13. for item_j in nums[left:bisect.bisect_right(nums, target // 2, left)]:
  14. item_k = target - item_j
  15. if item_k in count and item_k != item_j:
  16. ans.add((item_i, item_j, item_k))
  17. if count[item_i] > 1:
  18. if item_i == 0:
  19. if count[0] > 2:
  20. ans.add((0, 0, 0))
  21. else:
  22. if -2 * item_i in count:
  23. ans.add((item_i, item_i, -2 * item_i))
  24. return ans

Note:

  • bisect.bisect_left(a,x,lo=0,hi=len(a)) A function using binary search to find the target value x and return the index of it. If there are multiple x , return the first index. If there are none, return the index x should be insert in and remain the array sorted. a is the searching array. x is the target. lo and hi is the searching area of a.
  1. print("example of bisect_left")
  2. import bisect
  3. a=[0,1,2,3,3,4,6,7]
  4. x=4;print(bisect.bisect_left(a,x))
  5. #>>5
  6. x=3;print(bisect.bisect_left(a,x))
  7. #>>3
  8. x=5;print(bisect.bisect_left(a,x))
  9. #>>6
  10. x=5;print(bisect.bisect_left(a,x,2,4))
  11. #>>4

Explanation:

The solution is probably the fastest solution among the solutions in Leetcode. It is a close methon to the first solution above, but it reduce the searching area to (target+nums[-1],target//2), that reduces time spent in every for loop.