I am trying to find all unique triplets within a given set of integers that sum to zero. I have the following code which to me works logically but I keep getting a timeout error. Any advice on what I am missing or how to optimize? I have gone over it several times and I can't see how to make it any faster. Thanks in advance!
class Solution:
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
nums.sort()
for i in range(len(nums)-2):
if i > 0 and nums[i] == nums[i-1]:
continue
target = 0 - nums[i]
j = i +1
k = len(nums) - 1
tgt_sum = 0
trpls = []
while j < k:
tgt_sum = nums[j] + nums[k]
if tgt_sum == target:
x = []
x.append([i,j,k])
if all(item in trpls for item in x) == True:
trpls_list.append([i,j,k])
elif tgt_sum < target:
j += 1
elif tgt_sum > target:
k -= 1
else:
k -= 1
test = Solution()
given_nums = [-1,0,1,2,-1,-4]
print(test.threeSum(given_nums))
Related
When working with binary search algorithms, one updates one of the two pointers at each iteration.
However, there are cases like the LeetCode problem where this would miss the solution.
For example, the following solution of threeSumClosest works
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
nums.sort()
distance = float("inf")
for idx, num in enumerate(nums):
if num >= target:
l = 0
r = idx - 1
else:
l = idx + 1
r = len(nums) -1
while l < r:
res = num + nums[l] + nums[r]
if abs(target-res) < abs(distance):
distance = target - res
if res < target:
l +=1
else:
r -= 1
return target - distance
However, computing mid and using l = mid + 1 or r = mid - 1 misses the solution. How do you handle these cases?
I was expecting that updating l or r to mid +1 or mid -1 the algorithm would find the right solution
This question also appears in other forms, like finding the floor/ceiling of a number in a sorted list, or finding the insertion point of a number in a sorted list. In normal binary search if the middle element doesn’t match the predicate we look left or right, but in all of these problems we need to include it.
For example, given list [1, 2, 4], find insertion point of 3.
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
lo = 0
hi = len(nums) - 1
while lo < hi:
mid = lo + (hi - lo) // 2
if nums[mid] == target:
return mid
if nums[mid] < target:
lo = mid + 1
else:
hi = mid
return lo + int(nums[lo] < target)
Hi I'm trying to solve Leetcode 413: Arithmetic slices. I'm trying to start with a brute force recursive solution.
def numberOfArithmeticSlices(self, nums: List[int]) -> int:
def slices(nums: List[int], i: int):
if (i < 2):
return 0
if nums[i] - nums[i-1] == nums[i-1] - nums[i-2]:
return 1 + slices(nums, i -1)
else:
return slices(nums, i-1)
if len(nums) < 3:
return 0
return slices(nums, len(nums)-1)
This doesn't work for the test case [1,2,3,4] (it returns 2 instead of 3). In my head I know it doesn't work because when the function is called, 1 + slices([1,2,3], 2) returns 2. How can I fix my code to get the arithmetic slice coming from the entire array [1,2,3,4]?
For solving this problem you have to take two steps.
First you have to find all possible contiguous sub-arrays
You have to check them, if they are arithmetic slices.
An understandable solution which is not memory and time efficient is as below:
def numberOfArithmeticSlices(self, nums: List[int]) -> int:
if len(nums) <= 2:
return 0
sub_arrays = self.contiguous_subarray(nums) # type List[List[int]] all contiguous sub arrays with length 3 or more
count = 0
for subset in sub_arrays:
count = count + self.is_arithmetic_subset(subset)
return count
#staticmethod
def is_arithmetic_subset(subset):
if len(subset) <= 2:
return 0
diff = subset[1] - subset[0]
for i in range(2, len(subset)):
if subset[i] - subset[i - 1] != diff:
return 0
return 1
#staticmethod
def contiguous_subarray(nums):
return [nums[i:i + j] for i in range(0, len(nums)) for j in range(3, len(nums) - i + 1)]
But a solution that is little more harder to grasp but is memory and time efficient is as bellow(You could still replace the recursive call with a loop and I think you would get better results doing so):
def numberOfArithmeticSlices(self, nums: List[int]) -> int:
array_len = len(nums)
if array_len <= 2:
return 0
count = self.numberOfArithmeticSlices(nums[:array_len - 1])
diff = nums[array_len - 1] - nums[array_len - 2]
for i in range(2, array_len):
if nums[array_len - i ] - nums[array_len - i - 1] == diff:
count += 1
else:
break
return count
I'm implementing an usual merge sort algorithm in python as follows but got an unexpected type error.
from typing import List
def merge(arr1: List[int], arr2: List[int]) -> List[int]:
sorted_arr = []
i, j = 0, 0
while i < len(arr1) and j < len(arr2):
if arr1[i] < arr2[j]:
sorted_arr.append(arr1[i])
i = i + 1
else:
sorted_arr.append(arr2[j])
j = j + 1
sorted_arr += arr1[i:]
sorted_arr += arr2[j:]
def merge_sort(arr: List[int]):
if len(arr) == 1:
return arr
mid = len(arr) // 2
left_arr = merge_sort(arr[:mid])
right_arr = merge_sort(arr[mid:])
return merge(left_arr, right_arr)
The error says this:
while i < len(arr1) and j < len(arr2):
TypeError: object of type 'NoneType' has no len()
I have no idea why the type error can occur on the while loop. Could anyone help me with this? Thanks.
The problem is that you haven't explicitly returned anything from the merge() method and by default it returns None if nothing is returned explicitly.
Return the sorted_arr at the end of the merge() method as:
def merge(arr1: List[int], arr2: List[int]) -> List[int]:
sorted_arr = []
i, j = 0, 0
while i < len(arr1) and j < len(arr2):
if arr1[i] < arr2[j]:
sorted_arr.append(arr1[i])
i = i + 1
else:
sorted_arr.append(arr2[j])
j = j + 1
sorted_arr += arr1[i:]
sorted_arr += arr2[j:]
return sorted_arr # return the sorted_arr
Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
class Solution:
def threeSum(self, nums):
data = []
i = j = k =0
length = len(nums)
for i in range(length):
for j in range(length):
if j == i:
continue
for k in range(length):
if k == j or k == i:
continue
sorted_num = sorted([nums[i],nums[j],nums[k]])
if nums[i]+nums[j]+nums[k] == 0 and sorted_num not in data:
data.append(sorted_num)
return data
My soulution is working well but it appears that it may be too slow.
Is there a way to improve my codes without changing it significantly?
This is a O(n^2) solution with some optimization tricks:
import itertools
class Solution:
def findsum(self, lookup: dict, target: int):
for u in lookup:
v = target - u
# reduce duplication, we may enforce v <= u
try:
m = lookup[v]
if u != v or m > 1:
yield u, v
except KeyError:
pass
def threeSum(self, nums: List[int]) -> List[List[int]]:
lookup = {}
triplets = set()
for x in nums:
for y, z in self.findsum(lookup, -x):
triplets.add(tuple(sorted([x, y, z])))
lookup[x] = lookup.get(x, 0) + 1
return [list(triplet) for triplet in triplets]
First, you need a hash lookup to reduce your O(n^3) algorithm to O(n^2). This is the whole idea, and the rest are micro-optimizations:
the lookup table is build along with the scan on the array, so it is one-pass
the lookup table index on the unique items that seen before, so it handles duplicates efficiently, and by using that, we keep the iteration count of the second-level loop to the minimal
This is an optimized version, will pass through:
from typing import List
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
unique_triplets = []
nums.sort()
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
lo = i + 1
hi = len(nums) - 1
while lo < hi:
target_sum = nums[i] + nums[lo] + nums[hi]
if target_sum < 0:
lo += 1
if target_sum > 0:
hi -= 1
if target_sum == 0:
unique_triplets.append((nums[i], nums[lo], nums[hi]))
while lo < hi and nums[lo] == nums[lo + 1]:
lo += 1
while lo < hi and nums[hi] == nums[hi - 1]:
hi -= 1
lo += 1
hi -= 1
return unique_triplets
The TLE is most likely for those instances that fall into these two whiles:
while lo < hi and nums[lo] == nums[lo + 1]:
while lo < hi and nums[lo] == nums[lo + 1]:
References
For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis1, 2.
I'd suggest:
for j in range(i+1, length):
This will save you len(nums)^2/2 steps and first if statement becomes redundant.
sorted_num = sorted([nums[i],nums[j],nums[k]])
if nums[i]+nums[j]+nums[k] == 0 and sorted_num not in data:
sorted_num = sorted([nums[i],nums[j],nums[k]])
data.append(sorted_num)
To avoid unneeded calls to sorted in the innermost loop.
Your solution is the brute force one, and the slowest one.
Better solutions can be:
Assume you start from an element from array. Consider using a Set for finding next two numbers from remaining array.
There is a 3rd better solution as well. See https://www.gyanblog.com/gyan/coding-interview/leetcode-three-sum/
Hi I have drafted a recursive solution for the partition equal subset problem (https://leetcode.com/problems/partition-equal-subset-sum/) on LeetCode which is accepted:
class Solution(object):
def canPartition(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
if not nums or len(nums) < 2:
return False
if sum(nums) % 2 != 0:
return False
target = sum(nums) // 2
# if the maximum number in nums goes larger than target,
# then this maximum number cannot be partitioned into any subarray
# hence there's no solution in this case, return False early
if max(nums) > target:
return False
nums.sort(reverse = True)
return self.helper(nums, 0, target, 0)
def helper(self, nums, index, target, curr):
# since "target" value is derived from dividing total sum,
# we only need to search for one sum that makes target value in the array
# the rest is guaranteed to sum up to "target"
if curr == target:
return True
for i in range(index, len(nums)):
if curr + nums[i] > target:
continue
if self.helper(nums, i + 1, target, curr + nums[i]):
return True
return False
However, as a follow up, what would be the best way to actually return the two subsets instead of just True/False. What would the code look like with the saved subsets, if I had to update the above existing code? I am one of those people who are starting out with DP. Thanks in advance.
Figured out the solution:
class Solution(object):
def canPartition(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
if not nums or len(nums) < 2:
return []
if sum(nums) % 2 != 0:
return []
target = sum(nums) // 2
if max(nums) > target:
return []
nums.sort(reverse = True)
res = []
self.helper(nums, 0, target, [], res)
return res
def helper(self, nums, index, target, curr, res):
if sum(curr) == target:
res.append(list(curr))
return
for i in range(index, len(nums)):
if sum(curr) + nums[i] > target:
continue
self.helper(nums, i + 1, target, curr + [nums[i]], res)