Find Triplets smaller than a given number - python

I am trying to solve a problem where:
Given an array of n integers nums and a target, find the number of
index triplets i, j, k with 0 <= i < j < k < n that satisfy the
condition nums[i] + nums[j] + nums[k] < target.
For example, given nums = [-2, 0, 1, 3], and target = 2.
Return 2. Because there are two triplets which sums are less than 2:
[-2, 0, 1] [-2, 0, 3]
My algorithm: Remove a single element from the list, set target = target - number_1, search for doublets such that number_1 + number _2 < target - number_1. Problem solved.
The problem link is https://leetcode.com/problems/3sum-smaller/description/ .
My solution is:
def threeSumSmaller(nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
nums = sorted(nums)
smaller = 0
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
l, r = 0, len(temp) -1
t = target - nums[i]
while(l<r):
if temp[l] + temp[r] >= t:
r = r - 1
else:
smaller += 1
l = l + 1
return smaller
My solution fails:
Input:
[1,1,-2]
1
Output:
3
Expected:
1
I am not getting why is the error there as my solution passes more than 30 test cases.
Thanks for your help.

One main point is that when you sort the elements in the first line, you also lose the indexes. This means that, despite having found a triplet, you'll never be sure whether your (i, j, k) will satisfy condition 1, because those (i, j, k) do not come from the original list, but from the new one.
Additionally: everytime you pluck an element from the middle of the array, the remaining part of the array is also iterated (although in an irregular way, it still starts from the first of the remaining elements in tmp). This should not be the case! I'm expanding details:
The example iterates 3 times over the list (which is, again, sorted and thus you lose the true i, j, and k indexes):
First iteration (i = 0, tmp = [1, -2], t = 0).
When you sum temp[l] + temp[r] (l, r are 0, 1) it will be -1.
It satisfies being lower than t. smaller will increase.
The second iteration will be like the first, but with i = 1.
Again it will increase.
The third one will increase as well, because t = 3 and the sum will be 2 now.
So you'll count the value three times (despite only one tuple can be formed in order of indexes) because you are iterating through the permutations of indexes instead of combinations of them. So those two things you did not take care about:
Preserving indexes while sorting.
Ensuring you iterate the indexes in a forward-fashion only.
Try like this better:
def find(elements, upper_bound):
result = 0
for i in range(0, len(elements) - 2):
upper_bound2 = upper_bound - elements[i]
for j in range(i+1, len(elements) - 1):
upper_bound3 = upper_bound2 - elements[j]
for k in range(j+1, len(elements)):
upper_bound4 = upper_bound3 - elements[k]
if upper_bound4 > 0:
result += 1
return result

Seems like you're counting the same triplet more than once...
In the first iteration of the loop, you omit the first 1 in the list, and then increase smaller by 1. Then you omit the second 1 in the list and increase smaller again by 1. And finally you omit the third element in the list, -2, and of course increase smaller by 1, because -- well -- in all these three cases you were in fact considering the same triplet {1,1,-2}.
p.s. It seems like you care more about correctness than performance. In that case, consider maintaining a set of the solution triplets, to ensure you're not counting the same triplet twice.

There are already good answers , Apart that , If you want to check your algorithm result then you can take help of this in-built funtion :
import itertools
def find_(vector_,target):
result=[]
for i in itertools.combinations(vector_, r=3):
if sum(i)<target:
result.append(i)
return result
output:
print(find_([-2, 0, 1, 3],2))
output:
[(-2, 0, 1), (-2, 0, 3)]
if you want only count then:
print(len(find_([-2, 0, 1, 3],2)))
output:
2

Related

How to append to a list two numbers from within the list that add up to a number in the list?

First, I want to find the highest number in the list which is the second number in the list, then split it in two parts. The first part contains the 2nd highest number, while the second part contains the number from the list that sums to the highest number. Then, return the list
eg: input: [4,9,6,3,2], expected output:[4,6,3,6,3,2] 6+3 sums to 9 which is the highest number in the list
Please code it without itertools.
python
def length(s):
val=max(s)
s.remove(val)
for j in s:
if j + j == val:
s.append(j)
s.append(j)
return s
Here's what I have but it doesn't return what the description states.
Any help would be appreciated as I spent DAYS on this.
Thanks,
The main issue in your code seems to be that you are editing the list s whilst iterating through it, which can cause issues with the compiler and is generally just something you want to avoid doing in programming. A solution to this could be iterating through a copy of the original list.
The second problem is that your program doesn't actually find the second biggest value in the list, just a value which doubles to give you the biggest value.
The final problem (which I unfortunately only noticed after uploading what I thought was a solution) is that the split values are appended to the end of the list rather than to the position where originally the largest value was.
Hopefully this helps:
def length(array):
val = max(array)
idx = array.index(val) # gets the position of the highest value in the array (val)
array.remove(val)
for i in array.copy(): # creates a copy of the original list which we can iterate through without causing buggy behaviour
if max(array) + i == val:
array = array[:idx] + [max(array), i] + array[idx:]
# Redefines the list by placing inside of it: all values in the list upto the previous highest values, the 2 values we got from splitting the highest value, and all values which previously went after the highest value.
return array
This will return None if there is no value which can be added to the second highest value to get the highest value in the given array.
Input:
print(length([1,2,3,4,5]))
print(length([4,8,4,3,2]))
print(length([11,17,3,2,20]))
print(length([11,17,3,2,21]))
Output:
[1, 2, 3, 4, 4, 1]
[4, 4, 4, 4, 3, 2]
[11, 17, 3, 2, 17, 3]
None
Here are the docs on list slicing (which are impossible to understand) and a handy tutorial.
when you say "The first part contains the 2nd highest number" does that mean second highest number from the list or the larger of the two numbers that add up the largest number from list?
Here I assume you just wanted the larger of the two numbers that add up to the largest number to come first.
def length(s:list):
#start by finding the largest value and it's position in the list:
largest_pos = 0
for i in range(len(s)):
if s[i] > s[largest_pos]:
largest_pos = i
# find two numbers that add up to the largest number in the s
for trail in range(len(s)):
for lead in range(trail, len(s)):
if (s[trail] + s[lead]) == s[largest_pos]:
if s[trail] > s[lead]:
s[largest_pos] = s[trail]
s.insert(largest_pos +1, s[lead])
else:
s[largest_pos] = s[lead]
s.insert(largest_pos + 1, s[trail])
return s
# if no two numbers add up to the largest number. return s
return s
Since you are limited to 2 numbers, a simple nested loop works.
def length(s):
val = max(s)
idx = s.index(val)
s.remove(val)
for i in range(len(s) - 1):
for j in range(i + 1, len(s)):
if s[i] + s[j] == val:
s = s[:idx] + [s[i], s[j]] + s[idx:]
return s
print(length([4,9,6,3,2]))
Output:
[4, 6, 3, 6, 3, 2]
I used deque library
first to find the highest element or elements then remove all of them and replace them with second high value and rest like : 9 replace with 6 and 3 in example:
from collections import deque
l = [4, 9, 6, 3, 2]
a = deque(l)
e = a.copy()
s = max(a)
while s in a:
a.remove(s) # remove all highest elements
s2 = max(a) # find second high value
c = s - s2
for i in l:
if i == s:
w = e.index(i) # find index of high values
e.remove(max(e))
e.insert(w, s2)
e.insert(w+1, c)
print(list(e))

Consecutive repeated identical elements

I have a list that will consist of 1s and 0s. An example is: [1,1,1,1,1,0,1,0,1,0,0,0,0,0,1,1,1]
I am trying to print the number of times there were consecutive 1s. The desired output is 2. What I have so far:
def span(test_list):
res = []
for idx in range(0, len(test_list) - 1):
# getting Consecutive elements
if test_list[idx] == test_list[idx + 1]:
if test_list[idx]!=0:
res.append(test_list[idx])
# getting count of unique elements
res = len(list(set(res)))
# printing result
print("Consecutive identical elements count : " + str(res))
This however returns 1, where as I need the answer to be 2. Any help will be appreciated.
Just for fun... a solution using itertools.groupby:
test_list = [1,1,1,1,1,0,1,0,1,0,0,0,0,0,1,1,1]
sum(g[0] == 1 and len(g) > 1 for g in [list(g) for _, g in groupby(test_list)])
Output:
2
This works by grouping the consecutive identical values in the list which yields:
[1, 1, 1, 1, 1]
[0]
[1]
[0]
[1]
[0, 0, 0, 0, 0]
[1, 1, 1]
We then sum all the cases where the first element in the group is 1 and the length of the group is > 1.
#JonClements came up with an interesting way of doing this using itertools.islice which avoids the list conversion:
sum(next(islice(g, 1, None), 0) for k, g in groupby(test_list))
This works by taking a slice of the resultant group starting at the second element (islice(g, 1, None)) and then attempts to read that element (next(..., 0)) returning 0 if there isn't one. Thus next(islice(g, 1, None), 0) will only return 1 if the group is at least two 1's long.
Inspired by #JonClements' comment, I also came up with:
sum(next(g) & next(g, 0) for _, g in groupby(test_list))
This works by bitwise and'ing the first and second (or 0 if there isn't a second) elements in each grouped list, so only produces a 1 value if the grouped list contains more than one 1.
Timing wise, on my computer this is the fastest solution, with #JonClements about 20% slower and my original solution another 20% slower again.
Your problem here is that you are trying to add to your result list the sequences of consecutive ones, but there will be nothing to separate and distinguish the different sequences. So your result will look something like this:
[1,1,1,1,1,1]
Which will be reduce to just 1 when you convert it to a set.
My proposition is to track only the beginning of each consecutive sequence of ones, and to sum it like this:
def span(test_list):
res = []
started = False
for idx in range(0, len(test_list) - 1):
if test_list[idx] == test_list[idx + 1]:
if test_list[idx]!=0 and not started:
started = True
res.append(test_list[idx])
else:
started = False
res = sum(res)
print("Consecutive identical elements count : " + str(res))
test_liste = [1,1,1,1,1,0,1,0,1,0,0,0,0,0,1,1,1]
span(test_liste)
Why don't you use a counter that you increment each time list(n+1) == list(n)?
It will be easier than having to add each time an element to a list and then at the end to get the length of your list.
Apart from that, the problem you have is a logic problem. When you check that element n+1 is similar to your element n and that your element n is 1, you add only one element to your list (initially empty) while you checked that you had two similar elements.
My advice is to first check that the first element you read is a 1.
If it is not, then you go to the next one.
If it is, then you increment your counter by 1 and check the next one. If the next one is 1 then you increment your counter by 1. And so on.
What about it ? :
def span(test_list):
res = 0
for idx in range(0, len(test_list) - 1):
# check that you have two consecutive 1
if test_list[idx] == 1 and test_list[idx + 1] == 1:
res += 1;
# As long as the following values are 1, idx is incremented without incrementing res
while test_list[idx +1] == 1: //
idx += 1;
print("Consecutive identical elements count : " + str(res))

Smallest Missing Number

I'd like some insight on the following code. The problem says, devise an algorithm that finds the smallest missing number in an array
My Approach:
def small(arr: list) -> int:
s = {*arr}
i = 1
while True:
if i not in s:
return i
i += 1
Easy right?
The problem is this uses space complexity of (n) when I create that extra set
Better Approach:
# Linear time routine for partitioning step of Quicksort
def partition(arr):
pIndex = 0
# each time we find a positive number, `pIndex` is incremented, and
# that element would be placed before the pivot
for i in range(len(arr)):
if arr[i] > 0: # pivot is 0:
arr[i], arr[pIndex] = arr[pIndex], arr[i]
pIndex += 1
# return index of the first non-positive number
return pIndex
# Function to find the smallest missing positive number from an unsorted array
def findSmallestMissing(arr, n):
# Case 1. The missing number is in range 1 to `n`
# do for each array element
for i in range(n):
# get the value of the current element
val = abs(arr[i])
# make element at index `val-1` negative if it is positive
if val - 1 < n and arr[val - 1] >= 0:
arr[val - 1] = -arr[val - 1]
# check for missing numbers from 1 to `n`
for i in range(n):
if arr[i] > 0:
return i + 1
# Case 2. If numbers from 1 to `n` are present in the array,
# then the missing number is `n+1` e.g. [1, 2, 3, 4] —> 5
return n + 1
if __name__ == '__main__':
arr = [1, 4, 2, -1, 6, 5]
k = partition(arr)
print("The smallest positive missing number is",
findSmallestMissing(arr, k))
I don't understand why do we need
if val - 1 < n and arr[val - 1] >= 0:
arr[val - 1] = -arr[val - 1]
findSmallestMissing is really very similar to your set-based solution. The difference is that it uses the sign-bit in the input array as that set. That sign has nothing to do with the value that is stored at the same spot, but with the index. If the sign bit is set it means: I have encountered a value that is this index (after translating a value to an index by subtracting one).
The code you asked about:
if val - 1 < n and arr[val - 1] >= 0:
arr[val - 1] = -arr[val - 1]
First of all, the subtraction of one is there to covert a 1-based value to a 0-based index.
Since we use the array also as a set, this code first checks that the index is in range (of the "set") and then it checks that this index is not yet in the set, i.e. the sign bit at that index is still 0. If so, we add the index to the set, i.e. we set the sign bit at that index to 1.
All in all, one may argue that we cheat here: it is as if we didn't allocate extra memory for maintaining a set, but in reality we used an unused bit. In theory this means we still use extra memory, while in practice that memory was already allocated, but not used. The algorithm assumes that the none of the values in the array are negative.

Find missing elements in a list created from a sequence of consecutive integers with duplicates in O(n)

This is a Find All Numbers Disappeared in an Array problem from LeetCode:
Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array),
some elements appear twice and others appear once.
Find all the elements of [1, n] inclusive that do not appear in this array.
Could you do it without extra space and in O(n) runtime? You may
assume the returned list does not count as extra space.
Example:
Input:
[4,3,2,7,8,2,3,1]
Output:
[5,6]
My code is below - I think its O(N) but interviewer disagrees
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
results_list=[]
for i in range(1,len(nums)+1):
if i not in nums:
results_list.append(i)
return results_list
You can implement an algorithm where you loop through each element of the list and set each element at index i to a negative integer if the list contains the element i as one of the values,. You can then add each index i which is positive to your list of missing items. It doesn't take any additional space and uses at the most 3 for loops(not nested), which makes the complexity O(3*n), which is basically O(n). This site explains it much better and also provides the source code.
edit- I have added the code in case someone wants it:
#The input list and the output list
input = [4, 5, 3, 3, 1, 7, 10, 4, 5, 3]
missing_elements = []
#Loop through each element i and set input[i - 1] to -input[i - 1]. abs() is necessary for
#this or it shows an error
for i in input:
if(input[abs(i) - 1] > 0):
input[abs(i) - 1] = -input[abs(i) - 1]
#Loop through the list again and append each positive value to output list
for i in range(0, len(input)):
if input[i] > 0:
missing_elements.append(i + 1)
For me using loops is not the best way to do it because loops increase the complexity of the given problem. You can try doing it with sets.
def findMissingNums(input_arr):
max_num = max(input_arr) # get max number from input list/array
input_set = set(input_arr) # convert input array into a set
set_num = set(range(1,max(input_arr)+1)) #create a set of all num from 1 to n (n is the max from the input array)
missing_nums = list(set_num - input_set) # take difference of both sets and convert to list/array
return missing_nums
input_arr = [4,3,2,7,8,2,3,1] # 1 <= input_arr[i] <= n
print(findMissingNums(input_arr)) # outputs [5 , 6]```
Use hash table, or dictionary in Python:
def findDisappearedNumbers(self, nums):
hash_table={}
for i in range(1,len(nums)+1):
hash_table[i] = False
for num in nums:
hash_table[num] = True
for i in range(1,len(nums)+1):
if not hash_table[i]:
print("missing..",i)
Try the following :
a=input() #[4,3,2,7,8,2,3,1]
b=[x for x in range(1,len(a)+1)]
c,d=set(a),set(b)
print(list(d-c))

How can I re-write this while loop using nested for loops?

I followed an algorithm with a while loop, but one of the parameters of the question was that I use nested for loops, and I'm not sure how to do that.
This is the while loop:
i = len(lst)
while i > 0:
big = lst.index(max(lst[0:i]))
lst[big], lst[i-1] = lst[i-1], lst[big]
i = i - 1
return lst
This is the question it's answering:
Input: [5,1,7,3]
First, find the largest number, which is 7.
Swap it and the number currently at the end of the list, which is 3. Now we have: [5,1,3,7]
Now, find the largest number, not including the 7, which is 5.
Swap it and the second to last number, which is 3. Now we have: [3,1,5,7].
Now, find the third largest number (excluding the first two), which is 3.
Swap it and the third to last number, which is 1.
Output: [1, 3, 5, 7]
What you're seeing in the algorithm is a selection sort. And here's your second solution which you asked (nested for loops):
def insertion_sort(arr):
l = len(arr)
for i in range(l-1, -1, -1):
m = -10000 # it should be lower than min(arr)
idx = -1
for key, val in enumerate(arr[:i+1]):
if m < val:
m = val
idx = key
if idx != -1:
arr[i], arr[idx] = arr[idx], arr[i]
return arr
And a quick test:
arr = list(range(10))[::-1]
print(arr)
# prints [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
result = insertion_sort(arr)
print(result)
# prints [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
This looks like a (rather slow) sorting algorithm - namely bubble sort. It's iterating from the end of the list lst. Then it's searching for the maximum value in the first n-1 elements, and swapping them with the end. It will, however, fail, if the maximum value is already at the end, because then it will automatically swap the max(n-1) with the n value. You'll need to add a check for this.
So from a first look, I'm not sure if i is defined before, but let's assume it's defined at the length of the list lst, as it seems to be. So let's start with the outer loop - as have a while loop that looks like it's counting down from i to 0. This is the opposite of an increasing for-loop, so we can create a reserved range:
rev_range = range(0,len(lst))
rev_range.reverse()
for j in rev_range:
# perform the sort
We now have the outer loop for the counting-down while loop. The sort itself iterates forward until it finds the maximum. This is a forward for loop.
# sorting
max_val_so_far_index=lst[j]
# lst[:j-1] gets the first j-1 elements of the list
for k in lst[:j-1]:
if lst[k] > lst[max_val_so_far_index]:
max_val_so_far_index = k
# now we have the index of the maximum value
# swap
temp = lst[j]
lst[j] = lst[max_val_so_far_index]
lst[max_val_so_far_index]=temp
Let's put the two components together to get:
rev_range = range(0,len(lst))
rev_range.reverse()
for j in rev_range:
# perform the sort
# sorting
#print j
max_val_so_far_index=j
# get the first j items
for k in range(j):
if lst[k] > lst[max_val_so_far_index]:
max_val_so_far_index = k
# now we have the index of the maximum value
# swap
temp = lst[j]
lst[j] = lst[max_val_so_far_index]
lst[max_val_so_far_index]=temp
At the end lst is sorted.
The algorithm in the question is just another form of a bubble sort. The original algorithm uses two nested for loops. You can find a good explaination here.

Categories

Resources