[LeetCode][python3]0015. 3Sum
Start the journey
N2I -2020.03.21
- My first try
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
ans=[]
nums.sort()
#print(nums)
for i in range(len(nums)-2):
if nums[i]>0:
break
if i==0 or nums[i]>nums[i-1]:
j=i+1
k=len(nums)-1
while j<k:
if nums[i]+nums[j]+nums[k] == 0:
ans.append([nums[i],nums[j],nums[k]])
j+=1
k-=1
while j<k and nums[j]==nums[j-1]:
j+=1
while j<k and nums[k]==nums[k+1]:
k-=1
elif -nums[i]>nums[j]+nums[k]:
j+=1
else:
k-=1
#print(ans)
return ans
Explanation:
The classic way to solve this problem. The range between 500 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
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
if not nums:
return []
nums = sorted(nums)
res = []
self.kSum(nums, 0, len(nums)-1, 3, 0, [], res)
return res
def kSum(self, nums, left, right, k, target, state, res):
# if the selecting range is not enough
if k < 2 or right - left + 1 < k:
return
if k * nums[left] > target or k * nums[right] < target:
return
if k == 2:
# Solving 2Sum problem
while left < right:
cur = nums[left] + nums[right]
if cur == target:
res.append(state + [nums[left], nums[right]])
left += 1
while left < right and nums[left] == nums[left - 1]:
left += 1
elif cur > target:
right -= 1
else:
left += 1
else:
# k > 2, reduce the degree
for i in range(left, right+1):
if i == left or i > left and nums[i] != nums[i-1]:
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
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
ans = set()
count = {}
for i in nums:
if i not in count: count[i] = 1
else: count[i] += 1
nums = sorted(count)
for i, item_i in enumerate(nums):
if item_i < 0:
target = -item_i
left = bisect.bisect_left(nums, target - nums[-1], i + 1)
for item_j in nums[left:bisect.bisect_right(nums, target // 2, left)]:
item_k = target - item_j
if item_k in count and item_k != item_j:
ans.add((item_i, item_j, item_k))
if count[item_i] > 1:
if item_i == 0:
if count[0] > 2:
ans.add((0, 0, 0))
else:
if -2 * item_i in count:
ans.add((item_i, item_i, -2 * item_i))
return ans
Note:
bisect.bisect_left(a,x,lo=0,hi=len(a))
A function using binary search to find the target valuex
and return the index of it. If there are multiplex
, return the first index. If there are none, return the indexx
should be insert in and remain the array sorted.a
is the searching array.x
is the target.lo
andhi
is the searching area ofa
.
print("example of bisect_left")
import bisect
a=[0,1,2,3,3,4,6,7]
x=4;print(bisect.bisect_left(a,x))
#>>5
x=3;print(bisect.bisect_left(a,x))
#>>3
x=5;print(bisect.bisect_left(a,x))
#>>6
x=5;print(bisect.bisect_left(a,x,2,4))
#>>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.