How do I improve remove duplicate algorithm? - python

My interview question was that I need to return the length of an array that removed duplicates but we can leave at most 2 duplicates.
For example, [1, 1, 1, 2, 2, 3] the new array would be [1, 1, 2, 2, 3]. So the new length would be 5. I came up with an algorithm with O(2n) I believe. How can I improve that to be the fastest.
def removeDuplicates(nums):
if nums is None:
return 0
if len(nums) == 0:
return 0
if len(nums) == 1:
return 1
new_array = {}
for num in nums:
new_array[num] = new_array.get(num, 0) + 1
new_length = 0
for key in new_array:
if new_array[key] > 2:
new_length = new_length + 2
else:
new_length = new_length + new_array[key]
return new_length
new_length = removeDuplicates([1, 1, 1, 2, 2, 3])
assert new_length == 5
My first question would be is my algorithm even correct?

Your logic is correct however he is a simpler method to reach the goal you had mentioned in your question.
Here is my logic.
myl = [1, 1, 1, 2, 2, 3, 1, 1, 1, 2, 2, 3, 1, 1, 1, 2, 2, 3]
newl = []
for i in myl:
if newl.count(i) != 2:
newl.append(i)
print newl
[1, 1, 2, 2, 3, 3]
Hope this helps.

If your original array size is n.
Count distinct numbers in your array.
If you have d distinct numbers, then your answer will be
d (when n == d)
d+1 (when n == d+1)
d+2 (when n >= d+2)
If all the numbers in your array are less than n-1, you can even solve this without using any extra space. If that's the case check this and you can count distinct numbers very easily without using extra space.

I'd forget about generating the new array and just focus on counting:
from collections import Counter
def count_non_2dups(nums):
new_len = 0
for num, count in Counter(nums).items():
new_len += min(2, count)
return new_len

int removeDuplicates(vector<int>& nums) {
if (nums.size() == 0) return nums.size();
int state = 1;
int idx = 1;
for (int i = 1; i < nums.size(); ++i) {
if (nums[i] != nums[i-1]) {
state = 1;
nums[idx++] = nums[i];
}
else if (state == 1) {
state++;
nums[idx++] = nums[i];
}
else {
state++;
}
}
return idx;
}
idea:maintain a variable(state) recording the current repeat times(more precisely, state records the repeat times of the element which adjacent to the left of current element). This algorithm is O(n) with one scanning of the array.

def removeDuplicates(nums):
if nums is None:
return 0
if len(nums) == 0:
return 0
if len(nums) == 1:
return 1
new_array_a = set()
new_array_b = set()
while nums:
i = nums.pop()
if i not in new_array_a:
new_array_a.add(i)
elif i not in new_array_b:
new_array_b.add(i)
return len(new_array_a) + len(new_array_b)

Related

Obtain a strictly increasing sequence python

Question:
"Given a sequence of integers as an array, determine whether it is possible to obtain a strictly increasing sequence by removing no more than one element from the array.
Note: sequence a0, a1, ..., an is considered to be a strictly increasing if a0 < a1 < ... < an. Sequence containing only one element is also considered to be strictly increasing.
Examples:
For sequence = [1, 3, 2, 1], the output should be
solution(sequence) = false. There is no one element in this array that can be removed in order to get a strictly increasing sequence.
For sequence = [1, 3, 2], the output should be
solution(sequence) = true. You can remove 3 from the array to get the strictly increasing sequence [1, 2]. Alternately, you can remove 2 to get the strictly increasing sequence [1, 3]."
Here's my code:
def solution(sequence):
if len(sequence) == 1:
return True
else:
count = 0
for i in range(0,len(sequence) - 1):
if sequence[i] >= sequence[i + 1]:
count = count + 1
for i in range(0,len(sequence) - 2):
if sequence[i] >= sequence[i + 2]:
count = count + 1
return count <= 1
My code covers three cases:
Case 1: when the sequence is just one element long. I caught that in the first if statement.
Case 2: If there's more than one down-step – a case where the neighbour is less than the element being considered – then there is no way to adjust the sequence with just one removal, and so we get false (count > 1). I caught this in the first if statement.
Case 3: There are some cases, however, where there is only one down-step but there is still no way to remove just one element. This happens when the second element along is also less than the element being considered. For example, with [1,4,3,2] even if you removed the 3, you would still get a downstep. Now I covered this case by doing a second check, which checked whether the element two along is less, and if it is, then we add to the count.
Case 4: There is a case my code doesn't cover, which seems to be the only one, and that is when an element's neighbour and the next element along are both smaller than the element under consideration, but we could solve the issue just by getting rid of the element under consideration. So, with [1,4,2,3] both 2 and 3 are smaller than 4, but if we just got rid of the 4, then we're good. This case can occur either when the problem element is the first in the sequence or not. I'm not sure how to capture this properly. I suppose you might add in a conditional which looked at whether i-2 is less than i+1, but this won't work when i indexes the first element and it's quite cumbersome. I'm not sure how to go sort this out.
I'm quite sure I've overcomplicated things and what really is needed is to step back and think of a less piece-meal solution, but I'm stuck. Could anyone help? Note that we don't have to actually obtain the strictly increasing sequence; we just have to see whether we could.
Here is an idea you can a look at (edited after comments)
def distinct(L: list[int]) -> bool:
return len(L) == len(set(L))
def almost_increasing(L: list[int]) -> bool:
# some trivial cases
if len(L) <= 2: return True
if L[1: ] == sorted(L[1: ]) and distinct(L[1: ]): return True
if L[:-1] == sorted(L[:-1]) and distinct(L[:-1]): return True
return any(
L[ :i] == sorted(L[ :i]) and distinct(L[ :i]) and
L[i+1:] == sorted(L[i+1:]) and distinct(L[i+1:]) and
L[i-1 ] < L[i+1]
for i in range(1, len(L)-1)
)
And here is a nice way you can test it with hypothesis and pytest:
#given(L=st.lists(st.integers(), min_size=2, max_size=6))
def test_almost_increasing(L: list[int]):
expected = False
for i in range(len(L)):
Lcopy = L.copy()
del Lcopy[i]
expected |= (Lcopy == sorted(Lcopy) and distinct(Lcopy))
received = almost_increasing(L)
assert received == expected
Let's split the input into at most two increasing subsequences. If this is not possible, return false.
If there's only one sequence, return true.
If the length of either sequence is 1, the answer is true - we simply remove this element.
Otherwise we can join two sequences by either removing the last item from the first sequence, or the first item of the second one. That is,
if a[j-2] < a[j] -> ok, remove a[j - 1]
if a[j-1] < a[j + 1] -> ok, remove a[j]
where j is the index where the second sequence starts.
Code:
def check(a):
j = 0
for i in range(1, len(a)):
if a[i - 1] >= a[i]:
if j > 0:
return None
j = i
if j == 0:
return a
if j == 1:
return a[1:]
if j == len(a) - 1:
return a[:-1]
if a[j - 2] < a[j]:
return a[:(j - 1)] + a[j:]
if a[j - 1] < a[j + 1]:
return a[:j] + a[(j + 1):]
return None
assert check([2, 4, 6, 8]) == [2, 4, 6, 8], 'j == 0'
assert check([9, 4, 6, 8]) == [4, 6, 8], 'j == 1'
assert check([4, 6, 8, 1]) == [4, 6, 8], 'j == len-1'
assert check([2, 4, 9, 6, 8]) == [2, 4, 6, 8], 'j-2 < j'
assert check([2, 4, 1, 6, 8]) == [2, 4, 6, 8], 'j-1 < j+1'
assert check([2, 2, 2, 2]) is None, 'early return'
assert check([2, 8, 9, 6, 1]) is None, 'early return'
assert check([2, 4, 9, 3, 5]) is None, 'last return'
assert check([2]) == [2]
Try this
def solution(sequence):
n = len(sequence)
for i in range(n):
count = 0
trail = sequence[:]
del trail[i]
m = len(trail)
for j in range(m-1):
if trail[j] >= trail[j+1]:
count += 1
if count == 0:
return True
return False
This is not efficient nor optimized but it does the work.
Try this:
def is_solution(list_to_check):
if len(list_to_check) == 1:
return True
for i in range(1, len(list_to_check)):
if list_to_check[i] <= list_to_check[i - 1]:
new_list = list_to_check[:i - 1] + list_to_check[i:]
if (list_to_check[i] > list_to_check[i - 2]
and new_list == sorted(new_list)):
return True
elif (i == len(list_to_check) - 1
and list_to_check[:-1] == sorted(list_to_check[:-1])):
return True
return False
if __name__ == '__main__':
list_to_check = [1, 2, 1]
print(is_solution(list_to_check))
def solution(sequence):
"""determine strict increase"""
sequence_length = len(sequence)
if (
sequence_length == 0
or not len(set(sequence)) + 1 >= sequence_length
):
return False
return True

Fair partitioning of elements of a list

Given a list of ratings of players, I am required to partition the players (ie ratings) into two groups as fairly as possible. The goal is to minimize the difference between the teams' cumulative rating. There are no constraints as to how I can split the players into the teams (one team can have 2 players and the other team can have 10 players).
For example: [5, 6, 2, 10, 2, 3, 4] should return ([6, 5, 3, 2], [10, 4, 2])
I would like to know the algorithm to solve this problem. Please note I am taking an online programming introductory course, so simple algorithms would be appreciated.
I am using the following code, but for some reason, the online code checker says it is incorrect.
def partition(ratings):
set1 = []
set2 =[]
sum_1 = 0
sum_2 = 0
for n in sorted(ratings, reverse=True):
if sum_1 < sum_2:
set1.append(n)
sum_1 = sum_1 + n
else:
set2.append(n)
sum_2 = sum_2 + n
return(set1, set2)
Update: I contacted the instructors and I was told I should defined another "helper" function inside the function to check all different combinations then I need to check for the minimum difference.
Note: Edited to better handle the case when the sum of all numbers is odd.
Backtracking is a possibility for this problem.
It allows examining all the possibilities recursively, without the need of a large amount of memory.
It stops as soon as an optimal solution is found: sum = 0, where sum is the difference between the sum of elements of set A and the sum of elements of set B. EDIT: it stops as soon sum < 2, to handle the case when the sum of all numbers is odd, i.e. corresponding to a minimum difference of 1. If this global sum is even, the min difference cannot be equal to 1.
It allows to implement a simple procedure of premature abandon:
at a given time, if sum is higher then the sum of all remaining elements (i.e. not placed in A or B) plus the absolute value of current minimum obtained, then we can give up examining the current path, without examining the remaining elements. This procedure is optimized with:
sort the input data in decreasing order
A each step, first examine the most probable choice: this allow to go rapidly to a near-optimum solution
Here is a pseudo-code
Initialization:
sort elements a[]
Calculate the sum of remaining elements: sum_back[i] = sum_back[i+1] + a[i];
Set the min "difference" to its maximum value: min_diff = sum_back[0];
Put a[0] in A -> the index i of examined element is set to 1
Set up_down = true; : this boolean indicates if we are currently going forward (true) or backward (false)
While loop:
If (up_down): forward
Test premature abandon, with help of sum_back
Select most probable value, adjust sum according to this choice
if (i == n-1): LEAF -> test if the optimum value is improved and return if the new value is equal to 0 (EDIT: if (... < 2)); go backward
If not in a leaf: continue going forward
If (!updown): backward
If we arrive at i == 0 : return
If it is the second walk in this node: select the second value, go up
else: go down
In both cases: recalculate the new sum value
Here is a code, in C++ (Sorry, don't know Python)
#include <iostream>
#include <vector>
#include <algorithm>
#include <tuple>
std::tuple<int, std::vector<int>> partition(std::vector<int> &a) {
int n = a.size();
std::vector<int> parti (n, -1); // current partition studies
std::vector<int> parti_opt (n, 0); // optimal partition
std::vector<int> sum_back (n, 0); // sum of remaining elements
std::vector<int> n_poss (n, 0); // number of possibilities already examined at position i
sum_back[n-1] = a[n-1];
for (int i = n-2; i >= 0; --i) {
sum_back[i] = sum_back[i+1] + a[i];
}
std::sort(a.begin(), a.end(), std::greater<int>());
parti[0] = 0; // a[0] in A always !
int sum = a[0]; // current sum
int i = 1; // index of the element being examined (we force a[0] to be in A !)
int min_diff = sum_back[0];
bool up_down = true;
while (true) { // UP
if (up_down) {
if (std::abs(sum) > sum_back[i] + min_diff) { //premature abandon
i--;
up_down = false;
continue;
}
n_poss[i] = 1;
if (sum > 0) {
sum -= a[i];
parti[i] = 1;
} else {
sum += a[i];
parti[i] = 0;
}
if (i == (n-1)) { // leaf
if (std::abs(sum) < min_diff) {
min_diff = std::abs(sum);
parti_opt = parti;
if (min_diff < 2) return std::make_tuple (min_diff, parti_opt); // EDIT: if (... < 2) instead of (... == 0)
}
up_down = false;
i--;
} else {
i++;
}
} else { // DOWN
if (i == 0) break;
if (n_poss[i] == 2) {
if (parti[i]) sum += a[i];
else sum -= a[i];
//parti[i] = 0;
i--;
} else {
n_poss[i] = 2;
parti[i] = 1 - parti[i];
if (parti[i]) sum -= 2*a[i];
else sum += 2*a[i];
i++;
up_down = true;
}
}
}
return std::make_tuple (min_diff, parti_opt);
}
int main () {
std::vector<int> a = {5, 6, 2, 10, 2, 3, 4, 13, 17, 38, 42};
int diff;
std::vector<int> parti;
std::tie (diff, parti) = partition (a);
std::cout << "Difference = " << diff << "\n";
std::cout << "set A: ";
for (int i = 0; i < a.size(); ++i) {
if (parti[i] == 0) std::cout << a[i] << " ";
}
std::cout << "\n";
std::cout << "set B: ";
for (int i = 0; i < a.size(); ++i) {
if (parti[i] == 1) std::cout << a[i] << " ";
}
std::cout << "\n";
}
I think that you should do the next exercise by yourself, otherwise you don't learn much. As for this one, here is a solution that tries to implement the advice by your instructor:
def partition(ratings):
def split(lst, bits):
ret = ([], [])
for i, item in enumerate(lst):
ret[(bits >> i) & 1].append(item)
return ret
target = sum(ratings) // 2
best_distance = target
best_split = ([], [])
for bits in range(0, 1 << len(ratings)):
parts = split(ratings, bits)
distance = abs(sum(parts[0]) - target)
if best_distance > distance:
best_distance = distance
best_split = parts
return best_split
ratings = [5, 6, 2, 10, 2, 3, 4]
print(ratings)
print(partition(ratings))
Output:
[5, 6, 2, 10, 2, 3, 4]
([5, 2, 2, 3, 4], [6, 10])
Note that this output is different from your desired one, but both are correct.
This algorithm is based on the fact that, to pick all possible subsets of a given set with N elements, you can generate all integers with N bits, and select the I-th item depending on the value of the I-th bit. I leave to you to add a couple of lines in order to stop as soon as the best_distance is zero (because it can't get any better, of course).
A bit on bits (note that 0b is the prefix for a binary number in Python):
A binary number: 0b0111001 == 0·2⁶+1·2⁵+1·2⁴+1·2³+0·2²+0·2¹+1·2⁰ == 57
Right shifted by 1: 0b0111001 >> 1 == 0b011100 == 28
Left shifted by 1: 0b0111001 << 1 == 0b01110010 == 114
Right shifted by 4: 0b0111001 >> 4 == 0b011 == 3
Bitwise & (and): 0b00110 & 0b10101 == 0b00100
To check whether the 5th bit (index 4) is 1: (0b0111001 >> 4) & 1 == 0b011 & 1 == 1
A one followed by 7 zeros: 1 << 7 == 0b10000000
7 ones: (1 << 7) - 1 == 0b10000000 - 1 == 0b1111111
All 3-bit combinations: 0b000==0, 0b001==1, 0b010==2, 0b011==3, 0b100==4, 0b101==5, 0b110==6, 0b111==7 (note that 0b111 + 1 == 0b1000 == 1 << 3)
The following algorithm does this:
sorts the items
puts even members in list a, odd in list b to start
randomly moves and swaps items between a and b if the change is for the better
I have added print statements to show the progress on your example list:
# -*- coding: utf-8 -*-
"""
Created on Fri Dec 6 18:10:07 2019
#author: Paddy3118
"""
from random import shuffle, random, randint
#%%
items = [5, 6, 2, 10, 2, 3, 4]
def eq(a, b):
"Equal enough"
return int(abs(a - b)) == 0
def fair_partition(items, jiggles=100):
target = sum(items) / 2
print(f" Target sum: {target}")
srt = sorted(items)
a = srt[::2] # every even
b = srt[1::2] # every odd
asum = sum(a)
bsum = sum(b)
n = 0
while n < jiggles and not eq(asum, target):
n += 1
if random() <0.5:
# move from a to b?
if random() <0.5:
a, b, asum, bsum = b, a, bsum, asum # Switch
shuffle(a)
trial = a[0]
if abs(target - (bsum + trial)) < abs(target - bsum): # closer
b.append(a.pop(0))
asum -= trial
bsum += trial
print(f" Jiggle {n:2}: Delta after Move: {abs(target - asum)}")
else:
# swap between a and b?
apos = randint(0, len(a) - 1)
bpos = randint(0, len(b) - 1)
trya, tryb = a[apos], b[bpos]
if abs(target - (bsum + trya - tryb)) < abs(target - bsum): # closer
b.append(trya) # adds to end
b.pop(bpos) # remove what is swapped
a.append(tryb)
a.pop(apos)
asum += tryb - trya
bsum += trya - tryb
print(f" Jiggle {n:2}: Delta after Swap: {abs(target - asum)}")
return sorted(a), sorted(b)
if __name__ == '__main__':
for _ in range(5):
print('\nFinal:', fair_partition(items), '\n')
Output:
Target sum: 16.0
Jiggle 1: Delta after Swap: 2.0
Jiggle 7: Delta after Swap: 0.0
Final: ([2, 3, 5, 6], [2, 4, 10])
Target sum: 16.0
Jiggle 4: Delta after Swap: 0.0
Final: ([2, 4, 10], [2, 3, 5, 6])
Target sum: 16.0
Jiggle 9: Delta after Swap: 3.0
Jiggle 13: Delta after Move: 2.0
Jiggle 14: Delta after Swap: 1.0
Jiggle 21: Delta after Swap: 0.0
Final: ([2, 3, 5, 6], [2, 4, 10])
Target sum: 16.0
Jiggle 7: Delta after Swap: 3.0
Jiggle 8: Delta after Move: 1.0
Jiggle 13: Delta after Swap: 0.0
Final: ([2, 3, 5, 6], [2, 4, 10])
Target sum: 16.0
Jiggle 5: Delta after Swap: 0.0
Final: ([2, 4, 10], [2, 3, 5, 6])
Since I know I have to generate all possible lists, I need to make a "helper" function to help generate all possibilities. After doing that, I true to check for the minimum difference, and the combination of lists with that minimum difference is the desired solution.
The helper function is recursive, and check for all possibilities of combinations of lists.
def partition(ratings):
def helper(ratings, left, right, aux_list, current_index):
if current_index >= len(ratings):
aux_list.append((left, right))
return
first = ratings[current_index]
helper(ratings, left + [first], right, aux_list, current_index + 1)
helper(ratings, left, right + [first], aux_list, current_index + 1)
#l contains all possible sublists
l = []
helper(ratings, [], [], l, 0)
set1 = []
set2 = []
#set mindiff to a large number
mindiff = 1000
for sets in l:
diff = abs(sum(sets[0]) - sum(sets[1]))
if diff < mindiff:
mindiff = diff
set1 = sets[0]
set2 = sets[1]
return (set1, set2)
Examples:
r = [1, 2, 2, 3, 5, 4, 2, 4, 5, 5, 2], the optimal partition would be: ([1, 2, 2, 3, 5, 4], [2, 4, 5, 5, 2]) with a difference of 1.
r = [73, 7, 44, 21, 43, 42, 92, 88, 82, 70], the optimal partition would be: ([73, 7, 21, 92, 88], [44, 43, 42, 82, 70]) with a difference of 0.
Here is a fairly elaborate example, intended for educational purposes rather than performance. It introduces some interesting Python concepts such as list comprehensions and generators, as well as a good example of recursion in which fringe cases need to be checked appropriately. Extensions, e.g. only teams with an equal number of players are valid, are easy to implement in the appropriate individual functions.
def listFairestWeakTeams(ratings):
current_best_weak_team_rating = -1
fairest_weak_teams = []
for weak_team in recursiveWeakTeamGenerator(ratings):
weak_team_rating = teamRating(weak_team, ratings)
if weak_team_rating > current_best_weak_team_rating:
fairest_weak_teams = []
current_best_weak_team_rating = weak_team_rating
if weak_team_rating == current_best_weak_team_rating:
fairest_weak_teams.append(weak_team)
return fairest_weak_teams
def recursiveWeakTeamGenerator(
ratings,
weak_team=[],
current_applicant_index=0
):
if not isValidWeakTeam(weak_team, ratings):
return
if current_applicant_index == len(ratings):
yield weak_team
return
for new_team in recursiveWeakTeamGenerator(
ratings,
weak_team + [current_applicant_index],
current_applicant_index + 1
):
yield new_team
for new_team in recursiveWeakTeamGenerator(
ratings,
weak_team,
current_applicant_index + 1
):
yield new_team
def isValidWeakTeam(weak_team, ratings):
total_rating = sum(ratings)
weak_team_rating = teamRating(weak_team, ratings)
optimal_weak_team_rating = total_rating // 2
if weak_team_rating > optimal_weak_team_rating:
return False
elif weak_team_rating * 2 == total_rating:
# In case of equal strengths, player 0 is assumed
# to be in the "weak" team
return 0 in weak_team
else:
return True
def teamRating(team_members, ratings):
return sum(memberRatings(team_members, ratings))
def memberRatings(team_members, ratings):
return [ratings[i] for i in team_members]
def getOpposingTeam(team, ratings):
return [i for i in range(len(ratings)) if i not in team]
ratings = [5, 6, 2, 10, 2, 3, 4]
print("Player ratings: ", ratings)
print("*" * 40)
for option, weak_team in enumerate(listFairestWeakTeams(ratings)):
strong_team = getOpposingTeam(weak_team, ratings)
print("Possible partition", option + 1)
print("Weak team members: ", weak_team)
print("Weak team ratings: ", memberRatings(weak_team, ratings))
print("Strong team members:", strong_team)
print("Strong team ratings:", memberRatings(strong_team, ratings))
print("*" * 40)
Output:
Player ratings: [5, 6, 2, 10, 2, 3, 4]
****************************************
Possible partition 1
Weak team members: [0, 1, 2, 5]
Weak team ratings: [5, 6, 2, 3]
Strong team members: [3, 4, 6]
Strong team ratings: [10, 2, 4]
****************************************
Possible partition 2
Weak team members: [0, 1, 4, 5]
Weak team ratings: [5, 6, 2, 3]
Strong team members: [2, 3, 6]
Strong team ratings: [2, 10, 4]
****************************************
Possible partition 3
Weak team members: [0, 2, 4, 5, 6]
Weak team ratings: [5, 2, 2, 3, 4]
Strong team members: [1, 3]
Strong team ratings: [6, 10]
****************************************
Given that you want even teams you know the target score of the ratings of each team. This is the sum of the ratings divided by 2.
So the following code should do what you want.
from itertools import combinations
ratings = [5, 6, 2, 10, 2, 3, 4]
target = sum(ratings)/2
difference_dictionary = {}
for i in range(1, len(ratings)):
for combination in combinations(ratings, i):
diff = sum(combination) - target
if diff >= 0:
difference_dictionary[diff] = difference_dictionary.get(diff, []) + [combination]
# get min difference to target score
min_difference_to_target = min(difference_dictionary.keys())
strong_ratings = difference_dictionary[min_difference_to_target]
first_strong_ratings = [x for x in strong_ratings[0]]
weak_ratings = ratings.copy()
for strong_rating in first_strong_ratings:
weak_ratings.remove(strong_rating)
Output
first_strong_ratings
[6, 10]
weak_rating
[5, 2, 2, 3, 4]
There is other splits that have the same fairness these are all available to find inside the strong_ratings tuple, I just choose to look at the first one as this will always exist for any ratings list that you pass in (provided len(ratings) > 1).
A greedy solution might yield a sub-optimal solution. Here is a fairly simple greedy solution, the idea is to sort the list in descending order in order to decrease the effect of the addition of ratings in the bucket. Rating will be added to that bucket whose total rating sum is less
lis = [5, 6, 2, 10, 2, 3, 4]
lis.sort()
lis.reverse()
bucket_1 = []
bucket_2 = []
for item in lis:
if sum(bucket_1) <= sum(bucket_2):
bucket_1.append(item)
else:
bucket_2.append(item)
print("Bucket 1 : {}".format(bucket_1))
print("Bucket 2 : {}".format(bucket_2))
Output :
Bucket 1 : [10, 4, 2]
Bucket 2 : [6, 5, 3, 2]
Edit:
Another approach will be to generate all possible subsets of the list. Let's say you have l1 which is one of the subsets of the list, then you can easily get list l2 such that l2 = list(original) - l1. Number of all possible subset of the list of size n is 2^n. We can denote them as seq of an integer from 0 to 2^n -1. Take an example, say you have list = [1, 3, 5] then no of possible combination is 2^3 i.e 8. Now we can write all combination as follow:
000 - [] - 0
001 - [1] - 1
010 - [3] - 2
011 - [1,3] - 3
100 - [5] - 4
101 - [1,5] - 5
110 - [3,5]- 6
111 - [1,3,5] - 7
and l2, in this case, can easily be obtained by taking xor with 2^n-1.
Solution:
def sum_list(lis, n, X):
"""
This function will return sum of all elemenst whose bit is set to 1 in X
"""
sum_ = 0
# print(X)
for i in range(n):
if (X & 1<<i ) !=0:
# print( lis[i], end=" ")
sum_ += lis[i]
# print()
return sum_
def return_list(lis, n, X):
"""
This function will return list of all element whose bit is set to 1 in X
"""
new_lis = []
for i in range(n):
if (X & 1<<i) != 0:
new_lis.append(lis[i])
return new_lis
lis = [5, 6, 2, 10, 2, 3, 4]
n = len(lis)
total = 2**n -1
result_1 = 0
result_2 = total
result_1_sum = 0
result_2_sum = sum_list(lis,n, result_2)
ans = total
for i in range(total):
x = (total ^ i)
sum_x = sum_list(lis, n, x)
sum_y = sum_list(lis, n, i)
if abs(sum_x-sum_y) < ans:
result_1 = x
result_2 = i
result_1_sum = sum_x
result_2_sum = sum_y
ans = abs(result_1_sum-result_2_sum)
"""
Produce resultant list
"""
bucket_1 = return_list(lis,n,result_1)
bucket_2 = return_list(lis, n, result_2)
print("Bucket 1 : {}".format(bucket_1))
print("Bucket 2 : {}".format(bucket_2))
Output :
Bucket 1 : [5, 2, 2, 3, 4]
Bucket 2 : [6, 10]

using recursion to find the integer appearing odd times

I am looking for some guidance with the following code please. I am learning Python and I come from Java and C# where I was a beginner. I want to write a function which returns the number which appears an odd number of times. Assumption is that the array is always greater than 1 and there is always only one integer appearing an odd number of times. I want to use recursion.
The function does not return a value as when I store the result I get a NoneType. Please, I am not looking for a solution but some advice of where to look and how to think when debugging.
def find_it(seq):
seqSort = seq
seqSort.sort()
def recurfinder(arg,start,end):
seqSort = arg
start = 0
end = seqSort.length()-1
for i in range(start,end):
counter = 1
pos = 0
if seqSort[i+1] == seqSort[i]:
counter+=1
pos = counter -1
else:
if(counter % 2 == 0):
recurfinder(seqSort, pos+1, end)
else:
return seqSort[i]
return -1
You need to actually call recurFinder from somewhere outside of recurFinder to get the ball rolling.
def getOddOccurrence(arr, arr_size):
for i in range(0, arr_size):
count = 0
for j in range(0, arr_size):
if arr[i] == arr[j]:
count+= 1
if (count % 2 != 0):
return arr[i]
return -1
arr = [2, 3, 5, 4, 5, 2, 4, 3, 5, 2, 4, 4, 2 ]
n = len(arr)
print(getOddOccurrence(arr, n))
This answer uses recursion and a dict for fast counter lookups -
def find_it(a = [], i = 0, d = {}):
if i >= len(a):
return [ n for (n, count) in d.items() if count % 2 == 1 ]
else:
d = d.copy()
d[a[i]] = d.get(a[i], 0) + 1
return find_it(a, i + 1, d)
It works like this -
print(find_it([ 1, 2, 2, 2, 3, 3, 4, 5, 5, 5, 5 ]))
# [ 1, 2, 4 ]
print(find_it([ 1, 2, 3 ]))
# [ 1, 2, 3 ]
print(find_it([ 1, 1, 2, 2, 3, 3 ]))
# []
print(find_it([]))
# []
Above i and d are exposed at the call-site. Additionally, because we're relying on Python's default arguments, we have to call d.copy() to avoid mutating d. Using an inner loop mitigates both issues -
def find_it(a = []):
def loop(i, d):
if i >= len(a):
return [ n for (n, count) in d.items() if count % 2 == 1 ]
else:
d = d.copy()
d[a[i]] = d.get(a[i], 0) + 1
return loop(i + 1, d)
return loop(0, {})
It works the same as above.

Three sum algorithm solution

Original Problem Statement:
Given an array S of n integers, are there elements a, b, C in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note: The solution set must not contain duplicate triplets.
For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is: [[-1, 0, 1], [-1, -1, 2]]
I solved the Two Sum problem on LeetCode some time back and I thought of using it to solve the three sum as well. My idea is that for every element find two elements in the remaining list which sum up to element * -1 to get 0. However, this code doesn't pass all the test, for example
Input: [-4,-2,-2,-2,0,1,2,2,2,3,3,4,4,6,6]
Output: [[-4,-2,6],[-4,0,4],[-4,1,3],[-4,2,2]]
Expected: [[-4,-2,6],[-4,0,4],[-4,1,3],[-4,2,2],[-2,-2,4],[-2,0,2]]
I don't really know what's wrong. Can someone be kind enough to explain the problem to me.
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
def twoSum(self, nums, target):
targ = target
for index, i in enumerate(nums):
targ -= i
if targ in nums[index+1:]:
return [nums[index], nums[nums[index+1:].index(targ)+index+1]]
else:
targ = target
return None
res = []
for index, i in enumerate(nums):
target = i * -1
num = nums[:index] + nums [index+1:]
ans = twoSum(self, num, target)
if ans != None:
temp = ans + [i]
temp.sort()
res.append(temp)
print(res)
import itertools
res.sort()
res = list(res for res,_ in itertools.groupby(res))
return res
Original Question: https://leetcode.com/problems/3sum/description/
The one I'm using to solve this: https://leetcode.com/problems/two-sum/description/
using itertools.
import itertools
stuff = [-1, 0, 1, 2, -1, -4]
stuff.sort()
ls = []
for subset in itertools.combinations(stuff, 3):
if sum(list(subset))==0:
# first I have sorted the list because of grouping
# Ex: [-1, 0, 1] and [0, 1, -1] are build with the same element
# so here is avoiding this.
if list(subset) not in ls:
ls.append(list(subset))
print(ls)
input/output
input : [-1, 0, 1, 2, -1, -4]
output : [[-1, -1, 2], [-1, 0, 1]]
input : [-4,-2,-2,-2,0,1,2,2,2,3,3,4,4,6,6]
output: [[-4, -2, 6], [-4, 0, 4], [-4, 1, 3], [-4, 2, 2], [-2, -2, 4], [-2, 0, 2]]
Here's another way of solving it which has O(n^2) time complexity and passes the LeetCode test. It counts the occurrences and then sorts (number, count) tuples so [-1, 0, 1, 2, -1, -4] becomes [(-4, 1), (-1, 2), (0, 1), (1, 1), (2, 1)]. Then it iterates from beginning picking first trying to pick each number twice and third greater if possible and add this to result. Then it picks number once and tries to find two greater numbers which sum to 0.
from collections import Counter
class Solution(object):
def threeSum(self, nums):
res = []
counts = Counter(nums)
num_counts = sorted(counts.items())
# Handle the only case where we pick three same nums
if counts[0] >= 3:
res.append([0] * 3)
for i, (first, first_count) in enumerate(num_counts):
# Pick two of these and one greater
if first_count >= 2 and first < 0 and -(first * 2) in counts:
res.append([first, first, -(first * 2)])
# Pick one and two greater
for j in range(i + 1, len(num_counts)):
second, second_count = num_counts[j]
# Pick two of these as second and third num
if second_count >= 2 and -first == 2 * second:
res.append([first, second, second])
# Pick this as second num and third which is greater
third = -(first + second)
if third > second and third in counts:
res.append([first, second, third])
return res
One of the approach is using the HashSet, What I have try here:
public List<List<Integer>> threeSum(int[] nums) {
Set<List<Integer>> set = new HashSet<>();
Arrays.sort(nums);
for (int i = 0; i < nums.length - 1; i++) {
int j = i + 1;
int k = nums.length - 1;
while (j < k) {
int sum = nums[i] + nums[j] + nums[k];
if (sum == 0) {
set.add(Arrays.asList(nums[i], nums[j++], nums[k--]));
} else if (sum > 0) {
k--;
} else if (sum < 0) {
j++;
}
}
}
return new ArrayList<>(set);
}
}
I'd like to add the answer you claimed(in the comment) to post:
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
def twoSum(self, nums, target):
targ = target
for index, i in enumerate(nums):
targ -= i
# if targ in nums[index+1:]:
# return [nums[index], nums[nums[index+1:].index(targ)+index+1]]
_num = nums[:index] + nums[index+1:]
if targ in _num:
return [i, targ]
else:
targ = target
return None
res = []
for index, i in enumerate(nums):
target = i * -1
num = nums[:index] + nums [index+1:]
ans = twoSum(self, num, target)
if ans != None:
temp = ans + [i]
temp.sort()
res.append(temp)
print(res)
import itertools
res.sort()
res = list(res for res,_ in itertools.groupby(res))
return res
I have only run it my brain and hope it is right.

In what test cases could this fail to give the right output and what are potential solutions?

I need to take a list of integers and multiply its elements such that I get the greatest number possible. This number, I need to output as a string.
Example:
(int list) givenList = [2, 0, 2, 2, 0]
Output: 8
//because (2)*(2)*(2)=8
(int list) givenList= [-2, -3, 4, -5]
Output:60
//because (-3)*(-5)*(4)=60
Here is my code:
import operator
xs = [0,0]
if xs.count(0) == len(xs) :
print ("0")
elif xs.count(0) == len(xs)-1:
print ("0")
else:
negatives =[]
legits = []
listLength=len(xs)
counter = 0
while counter < listLength:
if xs[counter] <0:
negatives.append(xs[counter])
if xs[counter] > 0:
legits.append(xs[counter])
counter = counter +1
if len(negatives)%2 !=0:
negatives[negatives.index(max([n for n in negatives if n<0]))] = 1
newList = negatives + legits
print str((reduce(operator.mul, newList)))

Categories