I was going through this closest 3-sum leetcode problem which says:
Given an integer array nums of length n and an integer target, find three integers in nums such that the sum is closest to target.
Return the sum of the three integers.
You may assume that each input would have exactly one solution.
I have created the following solution and this appears correct but it fails with the Time Limit Exceeded error. How could I optimize this code? I have already added one of the optimization I felt but not sure how can I improve this further.
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
nums.sort()
csum = None
min_diff = float("+inf")
for i in range(0,len(nums)-2):
l = i + 1
r = len(nums)-1
if i > 0 and nums[i] == nums[i-1]:
continue # OPTIMIZATION TO AVOID SAME CALCULATION
while l < r:
sum = nums[i] + nums[l] + nums[r]
diff = abs(target-sum)
if sum == target:
csum = target
min_diff = 0
break
elif sum > target:
r -= 1
else:
l += 1
if min_diff > diff:
min_diff = diff
csum = sum
return nums[0] if csum is None else csum
Maybe this reference approach can help: Try it first and see if you have any question. Note - see this from a recent post, it performs really well - exceeds 90% of submission in Python category.
def threeSumClosest(self, nums: List[int], target: int) -> int:
nums.sort()
return self.kSumClosest(nums, 3, target)
def kSumClosest(self, nums: List[int], k: int, target: int) -> int:
N = len(nums)
if N == k: return sum(nums[:k]) # found it
# too small
tot = sum(nums[:k])
if tot >= target: return tot
# too big
tot = sum(nums[-k:])
if tot <= target: return tot
if k == 1:
return min([(x, abs(target - x)) for x in nums], key = lambda x: x[1])[0]
closest = sum(nums[:k])
for i, x in enumerate(nums[:-k+1]):
if i > 0 and x == nums[i-1]:
continue
current = self.kSumClosest(nums[i+1:], k-1, target - x) + x
if abs(target - current) < abs(target - closest):
if current == target:
return target
else:
closest = current
return closest
Related
I'm solving a problem on Leetcode. It must be solved in O(n*logn) time. I've used the Quicksort and Binary Search, but it went wrong on 17th test case in 18.
My code:
class Solution(object):
def sortArray(self, nums):
self.quickSort(nums, 0, len(nums)-1) #=> [arr, low, high] it's for binary search
return nums
def quickSort(self, arr, low, high):
mid = (low + high) // 2
arr[mid], arr[high] = arr[high], arr[mid] # pick the mid as pivot every time
if low < high:
pivot = self.partition(arr, low, high)
self.quickSort(arr, low, pivot-1)
self.quickSort(arr, pivot+1, high)
def partition(self, arr, low, high):
i = low
pivot = arr[high]
for n in range(low, high):
if arr[n] < pivot:
arr[i], arr[n] = arr[n], arr[i]
i += 1
arr[high], arr[i] = arr[i], arr[high]
return i `
More is here
Quicksort has a worst time complexity of O(𝑛²) and in the algorithm version you've chosen this worst case materialises when all values in the input are the same. You could try some other variants of quicksort, or you could first count the frequency of each number and then only sort the unique values:
def sortArray(self, nums):
d = {}
for n in nums:
if n in d:
d[n] += 1
else:
d[n] = 1
nums = list(d.keys())
self.quickSort(nums, 0, len(nums)-1)
return [n for n in nums for _ in range(d[n])]
This change to your sortArray will pass the tests, but its running time is not that great, nor its space usage.
I'd go for an algorithm whose worst case complexity is O(đť‘›logđť‘›). As the challenge also wants you to use the smallest space complexity possible, a standard merge sort is off the table (but some variants can go without extra memory). I will here suggest to use heap sort.
Below an implementation. I've also avoided the use of len and range so to take the instruction "without using any built-in functions" literally:
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
def length(): # As we are not allowed to use len()?
size = 0
for _ in nums:
size += 1
return size
def siftdown(i, size):
val = nums[i]
while True:
child = i*2 + 1
if child + 1 < size and nums[child+1] > nums[child]:
child += 1
if child >= size or val > nums[child]:
break
nums[i] = nums[child]
i = child
nums[i] = val
size = length()
# create max heap
i = size // 2
while i:
i -= 1
siftdown(i, size)
# heap sort
while size > 1:
size -= 1
nums[0], nums[size] = nums[size], nums[0]
siftdown(0, size)
return nums
This Python code is not working for some test cases on code wars two sum. Here is the link to the problem:
https://www.codewars.com/kata/52c31f8e6605bcc646000082/train/python
def two_sum(nums, target):
nums.sort()
l = 0
r = len(nums)-1
while l < r:
sum = nums[l] + nums[r]
if sum == target:
return [l, r]
if sum > target:
r -= 1
if sum < target:
l += 1
return []
Any help is much appreciated! :)
The solution you are looking for will be:
def two_sum(nums, target):
indices = {}
for index, num in enumerate(nums):
remainder = target - num
if remainder in indices:
return indices[remainder], index
indices[num] = index
return 0, 0
Right off the bat, I can also tell you that sorting nums before doing anything else is bad because the original indices can get mixed up.
def two_sum(numbers, target):
for n1 in enumerate(numbers):
for n2 in enumerate(numbers):
if n1[0] != n2[0]:
if (n1[1] + n2[1]) == target:
return [n1[0], n2[0]]
I decided to complete some tasks on Leetcode to improve my algorithm skills.
And I ran into a problem with LeetCode's problem 985. Sum of Even Numbers After Queries
Here is the description of that task:
You are given an integer array nums and an array queries where queries[i] = [valᵢ, indexᵢ].
For each query i, first, apply nums[indexᵢ] = nums[indexᵢ] + valᵢ, then print the sum of the even values of nums.
Return an integer array answer where answer[i] is the answer to the ith query.
Some samples of input and output:
# input1: nums = [1,2,3,4], queries = [[1,0],[-3,1],[-4,0],[2,3]]
# output1: [8, 6, 2, 4]
# input2: nums = [1], queries = [[4,0]]
# output2: [0]
So I found the solution, but as it has some huge samples of inputs, I found that my code is not efficient enough.
Here is my code:
class Solution:
def sumEvenAfterQueries(self, nums: list[int], queries: list[list[int]]) -> list[int]:
ans = []
integer_ans = 0
for i in range(len(queries)):
nums[queries[i][1]] = nums[queries[i][1]] + queries[i][0]
for j in nums:
if j % 2 == 0:
integer_ans += j
ans.append(integer_ans)
integer_ans = 0
return ans
So it does not solve problem because of the Time limit exceeded.
How can I improve my code to make it more efficient?
Try this solution. This will initially build up even_nums which are sum of even numbers in list, then incrementally update it with each value in queries.
It's probably not the most efficient, but it works for me. I submit on leetcode just now.
class Solution:
def sumEvenAfterQueries(self, nums: List[int], queries: List[List[int]]) -> List[int]:
even_sum = sum(x for x in nums if not x % 2)
result = []
for (add, i) in queries:
n = nums[i]
if add % 2: # odd
if n % 2: # both odd, result is even
even_sum += n + add
else: # even, add odd, result is odd
even_sum -= n
# odd, add even, result is still odd
# so, both must be even
elif not n % 2: # both even
even_sum += add
# remember updating nums at index #i
# and then adding to ours result
nums[i] = n + add
result.append(even_sum)
return result
Not so optimised solution, but you need to keep update of the sum of even values first and then for every query you run, you need to make the changes for the index of that value and see if sum for that index is calcualed or not. if not then add it to total sum and update the nums and if it is calculate then add the new sum value to it and modify the nums accordingly.
below is a simple way to achive this.
class Solution:
def sumEvenAfterQueries(self, nums: List[int], queries: List[List[int]]) -> List[int]:
dic = {i:v for i, v in enumerate(nums) if v%2==0}
sum_ = sum(dic.values())
res = []
for v, i in queries:
val = nums[i]
new_val = nums[i] + v
if i not in dic:
if new_val%2==0:
dic[i] = new_val
sum_ += new_val
res.append(sum_)
else:
res.append(sum_)
else:
if new_val%2==0:
sum_ -= val
sum_ += new_val
dic[i] = new_val
res.append(sum_)
else:
sum_-= val
del dic[i]
res.append(sum_)
nums[i] = new_val
return res
This is an addition to rv.kvetch answer
Your code is giving TLE because it has the worst case time complexity of O(n2). I hope you know about the it.
Coming to the solution, we can do in in O(n) i.e. linear time.
Algorithm
Initially we will store sum of all even numbers present in nums to all_sum
Adding value to any item of nums list can make nums[index] + value even or odd, we will check these conditions
Based on these conditions checks, we either add (value or nums[index] + value) or subtract nums[index] from all_sum
After all these condition checks we will modify nums[index] to be nums[index] = nums[index] + value and add all_sum to the array that we want to return as answer
Code
def sumEvenAfterQueries(self, nums: List[int], queries: List[List[int]]) -> List[int]:
all_sum = 0
for e in nums:
if e%2 == 0:
all_sum += e
sum_arr = []
for val, index in queries:
if nums[index] % 2 == 0: # nums item is already added to all_sum
if (nums[index] + val) % 2 == 0:
all_sum += val
else:
all_sum -= nums[index]
else: # nums item is odd so it wasn't added to all_sum before
if (nums[index] + val) % 2 == 0:
all_sum += (nums[index] + val)
else:
pass
nums[index] += val
sum_arr.append(all_sum)
return sum_arr
The question, https://leetcode.com/problems/first-missing-positive/, asks:
Given an unsorted integer array nums, return the smallest missing positive integer.
You must implement an algorithm that runs in O(n) time and uses constant extra space.
Example 1:
Input: nums = [1,2,0]
Output: 3
Example 2:
Input: nums = [3,4,-1,1]
Output: 2
Example 3:
Input: nums = [7,8,9,11,12]
Output: 1
Constraints:
1 <= nums.length <= 5 * 10**5
-2**31 <= nums[i] <= 2**31 - 1
Thus my code satisfies this:
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
nums=sorted(list(filter(lambda x: x>=0, nums)))
nums= list(dict.fromkeys(nums))
if 1 not in nums: return 1
x=nums[0]
for num in nums:
if nums.index(num) != 0:
dif = num - x
if dif!=1:
return x + 1
x=num
return num+1
Glad for anyone to offer help.
As the comments described, sorted() doesn't take linear time. sorted() also creates a new list, so your solution also violates the O(1) memory constraint.
Here's a linear-time, constant-space solution. The problem asks for two things (for simplicity, let n = len(nums)):
a data structure that can in O(1) time, determine whether a positive integer in the interval [1, n] is in nums. (We have n numbers to check, and the runtime of our algorithm has to be linear.) For this problem, our strategy is to create a table such that for every integer i between 1 and n, if i is in nums, then nums[i - 1] = i. (The answer has to be positive, and the answer can't be greater than n + 1 -- the only way for the answer to be n + 1 is if nums contains every integer in the interval [1, n]).
a procedure to generate the data structure in-place to meet the memory constraint.
Here's a solution that does this.
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
# Match elements to their indicies.
for index, num in enumerate(nums):
num_to_place = num
while num_to_place > 0 and num_to_place <= len(nums) and num_to_place != nums[num_to_place - 1]:
next_num_to_place = nums[num_to_place - 1]
nums[num_to_place - 1] = num_to_place
num_to_place = next_num_to_place
# Find smallest number that doesn't exist in the array.
for i in range(len(nums)):
if nums[i] != i + 1:
return i + 1
return len(nums) + 1
Both for loops takes linear time. The reasoning for the second is obvious, but the time analysis of the first is a bit more subtle:
Notice that the while loop contains this condition: num_to_place != nums[num_to_place - 1]. For each iteration of this while loop, the number of values that meet this condition decreases by 1. So, this while loop can only execute at most n times across all iterations, meaning the first for loop takes O(n) time.
# O(n) time and O(1) space
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
index = 0
while index < len(nums):
if nums[index] > 0 and nums[index] - 1 < len(nums) and nums[index] != nums[nums[index] - 1]:
nums[nums[index]-1], nums[index] = nums[index], nums[nums[index] - 1]
else:
index += 1
for i, integer in enumerate(nums):
if integer != i + 1:
return i + 1
return len(nums) + 1
I am trying to solve a problem where,
Given an array S of n integers, find three integers in S such that the
sum is closest to a given number, target. Return the sum of the three
integers. You may assume that each input would have exactly one
solution.
For example, given array S = {-1 2 1 -4}, and target = 1.
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
My solution is: take a number out (number_1) from the array, set the target to target - that number and find two other numbers which are closest to the new target. This way: number_1 + number_2 + number_3 will be closest as number_2 + number_3 will be closest to target - number_1.
I tried my solution at https://leetcode.com/problems/3sum-closest/description/.
My solution is:
def threeSumClosest(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
closest_sum = nums[0] + nums[1] + nums[2]
for i in range(len(nums)):
# Create temp array excluding a number
if i!=len(nums)-1:
temp = nums[:i] + nums[i+1:]
else:
temp = nums[:len(nums)-1]
# Sort the temp array and set new target to target - the excluded number
temp = sorted(temp)
l, r = 0, len(temp) -1
t = target - nums[i]
while(l<r):
if temp[l] + temp[r] == t:
return target
elif temp[l] + temp[r] > t:
if abs(temp[l] + temp[r] + nums[i] - target) < abs(closest_sum - target):
closest_sum = temp[l] + temp[r] + nums[i]
r = r - 1
else:
if abs(temp[l] + temp[r] + nums[i] - target) < abs(closest_sum - target):
closest_sum = temp[l] + temp[r] + nums[i]
l = l + 1
return closest_sum
It passes 80 test cases out of 125, so the solution logic looks fine enough for me.
It fails for:
Input:
[0,2,1,-3]
1
Output:
3
Expected:
0
Can't understand why it fails and how to make my logic consistent.
Thanks for your help.
You have couple of mistakes the first one is silly, you have an extra indentation in return closest and the second one is not checking updating closest in the 3rd if statement.
This code got accepted:
class Solution(object):
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
closest = nums[0] + nums[1] + nums[2]
#if len(nums)==3:
# return closest
for i in range(len(nums)):
if i!=len(nums)-1:
temp = nums[:i] + nums[i+1:]
else:
temp = nums[:len(nums)-1]
temp = sorted(temp)
l, r = 0, len(temp) -1
t = target - nums[i]
while(l < r):
if abs(temp[l] + temp[r] + nums[i] - target) < abs(closest - target):
closest = temp[l] + temp[r] + nums[i]
if temp[l] + temp[r] == t:
return target
elif temp[l] + temp[r] > t:
r = r - 1
else:
l = l + 1
return closest
And this is an accepted C++ solution with O(n^2) running time:
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
int ans = nums[0] + nums[1] + nums[2];
for(int i = 0; i < nums.size() - 2; i++) {
int l = i + 1, r = nums.size() - 1;
while (l < r) {
if(abs(nums[i] + nums[l] + nums[r] - target) < abs(target - ans)) {
ans = nums[i] + nums[l] + nums[r];
}
if(nums[r] + nums[l] > target - nums[i]) r = r - 1;
else l = l + 1;
}
}
return ans;
}
};
As we worked out in the comments the last return statment was erroneously inside the for loop cutting it short after the first iteration.
Also, closest should be updated in both branches where we overshoot or undershoot the target.
I think an obvious improvement of your algorithm would be to sort first. Removing individual elements doesn't destroy order, so you'd need to sort only once. That would get you from O(n^2 log n) to O(n^2).
You can find all combinations of values in the list, and then find the listing whose sum is closest to the target:
import itertools
s = [{'vals':[-1, 2, 1, -4], 'target':1}, {'vals':[0,2,1,-3],'target':1}]
final_result = {tuple(a['vals']):sum(min(itertools.combinations(a['vals'], 3), key=lambda x:abs(a['target']-sum(x)))) for a in s}
Output:
{(-1, 2, 1, -4): 2, (0, 2, 1, -3): 0}
My solution works for this input:
[0,2,1,-3]
1
Your closest sum variable is incorrect. See my variable named "gap"
class Solution:
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
sorted_nums = sorted(nums)
gap = sorted_nums[len(nums)-1] * 10000
solution = 0
for pointer_1 in range(len(sorted_nums)):
pointer_2 = pointer_1 + 1
pointer_3 = len(sorted_nums) - 1
while(pointer_2 < pointer_3):
gap_n = abs((sorted_nums[pointer_1]+sorted_nums[pointer_2]+sorted_nums[pointer_3]) - target)
add = (sorted_nums[pointer_1]+sorted_nums[pointer_2]+sorted_nums[pointer_3])
if (gap_n < gap):
solution = add
gap = gap_n
elif (target > add):
pointer_2 = pointer_2 + 1
else:
pointer_3 = pointer_3 - 1
return solution