printing K lowest values in Binary search tree - python

I am trying to figure out how to print the lowest k values in a binary search tree. I an having trouble stopping the method
code:
def kthSmallestBST(node,k, count):
if node == None or count == k:
return
else:
kthSmallestBST(node.left, k, count)
count += 1
print node.data
kthSmallestBST(node.right, k, count)
count += 1
kthSmallestBST(BST, 3, 0)
Currently my output simply printed the whole tree inorder

It's a rather "functional programming" solution, but one way is to generate (lazily) the nodes in the tree in order, and then using itertools to just take the first k.
def inorder(tree):
if not tree: return
for node in inorder(tree.left): yield node
yield tree
for node in inorder(tree.right): yield node
def least(tree, k):
return itertools.islice(inorder(tree), k)
If you're using Python 3, you can use "yield from" to make this solution shorter.

Changes to the value of count don't propagate back up to the caller. You need to return the new count:
def kthSmallestBST(node,k, count):
if node is None or count >= k:
return 0
else:
count += kthSmallestBST(node.left, k, count)
if count < k:
print node.data
count += 1
count += kthSmallestBST(node.right, k, count)
return count
Also note you don't need both k and count. You can get rid of count, decrement k instead of incrementing count, and compare k against 0 (instead of against count). Here's what you get:
def kthSmallestBST(node, k):
if node is None or k <= 0:
return 0
k = kthSmallestBST(node.left, k)
if k > 0:
print node.data
k -= 1
k = kthSmallestBST(node.right, k)
return k

You need to change things around a bit so you know how many elements were found during the recursive call. Have the function return the number of elements it found an add them up. Also you need to check the count between the recursive calls and the current node's element.
Something like:
def kthSmallestBST(node, k, count):
if node == None or count == k:
return 0
else:
count += kthSmallestBST(node.left, k, count)
if(count == k)
return count
print node.data
count += 1
count += kthSmallestBST(node.right, k, count)
return count

import unittest
class BST(object):
def __init__(self, key):
self.key = key
self.left = None
self.right = None
def get_kth_smallest_keys(node, k):
"""Return, as a list, `k` smallest keys in a binary search tree rooted at `node`.
"""
smallest = []
_get_kth_smallest_keys(node, k, smallest)
return smallest
def _get_kth_smallest_keys(node, k, smallest):
"""A helper function. Appends nodes to the given list, `smallest`, and stop
when k reaches 0.
Returns the number of nodes appended to said list.
"""
if node is None or k == 0:
return 0
# first, recurse left, and we get the number of nodes appended to the list by that call
nk = _get_kth_smallest_keys(node.left, k, smallest)
# if that number already reduces our counter to zero, we fail fast, returning that same number
if k - nk <= 0:
return nk
# otherwise, we can still append this node's key to the list
smallest.append(node.key)
# then we recurse right, with a counter that is less 1 (our append) and less nk (appended by the left recurse)
nnk = _get_kth_smallest_keys(node.right, k - 1 - nk, smallest)
# our return value is the sum of our append (1) and the appends from both recurse calls
return nk + 1 + nnk
class BSTTest(unittest.TestCase):
def test_smallest_keys(self):
root = BST(10)
root.left = BST(6)
root.right = BST(15)
root.left.right = BST(8)
root.right.right = BST(20)
self.assertEquals(get_kth_smallest_keys(root, 0), [])
self.assertEquals(get_kth_smallest_keys(root, 1), [6])
self.assertEquals(get_kth_smallest_keys(root, 3), [6, 8, 10])
self.assertEquals(get_kth_smallest_keys(root, 5), [6, 8, 10, 15, 20])
self.assertEquals(get_kth_smallest_keys(root, 6), [6, 8, 10, 15, 20])
if __name__ == '__main__':
unittest.main()

Related

Partition of a list of integers into K sublists with equal sum

Similar questions are 1 and 2 but the answers didn't help.
Assume we have a list of integers. We want to find K disjoint lists such that they completely cover the given list and all have the same sum. For example, if A = [4, 3, 5, 6, 4, 3, 1] and K = 2 then the answer should be:
[[3, 4, 6], [1, 3, 4, 5]]
or
[[4, 4, 5], [1, 3, 3, 6]]
I have written a code that only works when K = 2 and it works fine with small lists as input but with very larger lists, because of the code's high complexity, OS terminates the task. My code is:
def subarrays_equal_sum(l):
from itertools import combinations
if len(l) < 2 or sum(l) % 2 != 0:
return []
l = sorted(l)
list_sum = sum(l)
all_combinations = []
for i in range(1, len(l)):
all_combinations += (list(combinations(l, i)))
combinations_list = [i for i in all_combinations if sum(i) == list_sum / 2]
if not combinations_list:
return []
final_result = []
for i in range(len(combinations_list)):
for j in range(i + 1, len(combinations_list)):
first = combinations_list[i]
second = combinations_list[j]
concat = sorted(first + second)
if concat == l and [list(first), list(second)] not in final_result:
final_result.append([list(first), list(second)])
return final_result
An answer for any value of K is available here. But if we pass the arguments A = [4, 3, 5, 6, 4, 3, 1] and K = 2, their code only returns [[5, 4, 3, 1],[4, 3, 6]] whereas my code returns all possible lists i.e.,
[[[3, 4, 6], [1, 3, 4, 5]], [[4, 4, 5], [1, 3, 3, 6]]]
My questions are:
How to improve the complexity and cost of my code?
How to make my code work with any value of k?
Here is a solution that deals with duplicates.
First of all the problem of finding any solution is, as noted, NP-complete. So there are cases where this will churn for a long time to realize that there are none. I've applied reasonable heuristics to limit how often this happens. The heuristics can be improved. But be warned that there will be cases that simply nothing works.
The first step in this solution is to take a list of numbers and turn it into [(value1, repeat), (value2, repeat), ...]. One of those heuristics requires that the values be sorted first by descending absolute value, and then by decreasing value. That is because I try to use the first elements first, and we expect a bunch of small leftover numbers to still give us sums.
Next, I'm going to try to split it into a possible maximal subset with the right target sum, and all remaining elements.
Then I'm going to split the remaining into a possible maximal remaining subset that is no bigger than the first, and the ones that result after that.
Do this recursively and we find a solution. Which we yield back up the chain.
But, and here is where it gets tricky, I'm not going to do the split by looking at combinations. Instead I'm going to use dynamic programming like we would for the usual subset-sum pseudo-polynomial algorithm, except I'll use it to construct a data structure from which we can do the split. This data structure will contain the following fields:
value is the value of this element.
repeat is how many times we used it in the subset sum.
skip is how many copies we had and didn't use it in the subset sum.
tail is the tail of these solutions.
prev are some other solutions where we did something else.
Here is a class that constructs this data structure, with a method to split elements into a subset and elements still available for further splitting.
from collections import namedtuple
class RecursiveSums (
namedtuple('BaseRecursiveSums',
['value', 'repeat', 'skip', 'tail', 'prev'])):
def sum_and_rest(self):
if self.tail is None:
if self.skip:
yield ([self.value] * self.repeat, [(self.value, self.skip)])
else:
yield ([self.value] * self.repeat, [])
else:
for partial_sum, rest in self.tail.sum_and_rest():
for _ in range(self.repeat):
partial_sum.append(self.value)
if self.skip:
rest.append((self.value, self.skip))
yield (partial_sum, rest)
if self.prev is not None:
yield from self.prev.sum_and_rest()
You might have to look at this a few times to see how it works.
Next, remember I said that I used a heuristic to try to use large elements before small ones. Here is some code that we'll need to do that comparison.
class AbsComparator(int):
def __lt__ (self, other):
if abs(int(self)) < abs(int(other)):
return True
elif abs(other) < abs(self):
return False
else:
return int(self) < int(other)
def abs_lt (x, y):
return AbsComparator(x) < AbsComparator(y)
We'll need both forms. The function for a direct comparison, the class for Python's key argument to the sort function. See Using a comparator function to sort for more on the latter.
And now the heart of the method. This finds all ways to split into a subset (that is no larger than bound in the comparison metric we are using) and the remaining elements to split more.
The idea is the same as the dynamic programming approach to subset sum https://www.geeksforgeeks.org/count-of-subsets-with-sum-equal-to-x/ except with two major differences. The first is that instead of counting the answers we are building up our data structure. The second is that our keys are (partial_sum, bound_index) so we know whether our bound is currently satisfied, and if it is not we know what element to compare next to test it.
def lexically_maximal_subset_rest (elements, target, bound=None):
"""
elements = [(value, count), (value, count), ...]
with largest absolute values first.
target = target sum
bound = a lexical bound on the maximal subset.
"""
# First let's deal with all of the trivial cases.
if 0 == len(elements):
if 0 == target:
yield []
elif bound is None or 0 == len(bound):
# Set the bound to something that trivially works.
yield from lexically_maximal_subset_rest(elements, target, [abs(elements[0][0]) + 1])
elif abs_lt(bound[0], elements[0][0]):
pass # we automatically use more than the bound.
else:
# The trivial checks are done.
bound_satisfied = (bound[0] != elements[0][0])
# recurse_by_sum will have a key of (partial_sum, bound_index).
# If the bound_index is None, the bound is satisfied.
# Otherwise it will be the last used index in the bound.
recurse_by_sum = {}
# Populate it with all of the ways to use the first element at least once.
(init_value, init_count) = elements[0]
for i in range(init_count):
if not bound_satisfied:
if len(bound) <= i or abs_lt(bound[i], init_value):
# Bound exceeded.
break
elif abs_lt(init_value, bound[i]):
bound_satisfied = True
if bound_satisfied:
key = (init_value * (i+1), None)
else:
key = (init_value * (i+1), i)
recurse_by_sum[key] = RecursiveSums(
init_value, i+1, init_count-i-1, None, recurse_by_sum.get(key))
# And now we do the dynamic programming thing.
for j in range(1, len(elements)):
value, repeat = elements[j]
next_recurse_by_sum = {}
for key, tail in recurse_by_sum.items():
partial_sum, bound_index = key
# Record not using this value at all.
next_recurse_by_sum[key] = RecursiveSums(
value, 0, repeat, tail, next_recurse_by_sum.get(key))
# Now record the rest.
for i in range(1, repeat+1):
if bound_index is not None:
# Bounds check.
if len(bound) <= bound_index + i:
break # bound exceeded.
elif abs_lt(bound[bound_index + i], value):
break # bound exceeded.
elif abs_lt(value, bound[bound_index + i]):
bound_index = None # bound satisfied!
if bound_index is None:
next_key = (partial_sum + value * i, None)
else:
next_key = (partial_sum + value * i, bound_index + i)
next_recurse_by_sum[next_key] = RecursiveSums(
value, i, repeat - i, tail, next_recurse_by_sum.get(next_key))
recurse_by_sum = next_recurse_by_sum
# We now have all of the answers in recurse_by_sum, but in several keys.
# Find all that may have answers.
bound_index = len(bound)
while 0 < bound_index:
bound_index -= 1
if (target, bound_index) in recurse_by_sum:
yield from recurse_by_sum[(target, bound_index)].sum_and_rest()
if (target, None) in recurse_by_sum:
yield from recurse_by_sum[(target, None)].sum_and_rest()
And now we implement the rest.
def elements_split (elements, target, k, bound=None):
if 0 == len(elements):
if k == 0:
yield []
elif k == 0:
pass # still have elements left over.
else:
for (subset, rest) in lexically_maximal_subset_rest(elements, target, bound):
for answer in elements_split(rest, target, k-1, subset):
answer.append(subset)
yield answer
def subset_split (raw_elements, k):
total = sum(raw_elements)
if 0 == (total % k):
target = total // k
counts = {}
for e in sorted(raw_elements, key=AbsComparator, reverse=True):
counts[e] = 1 + counts.get(e, 0)
elements = list(counts.items())
yield from elements_split(elements, target, k)
And here is a demonstration using your list, doubled. Which we split into 4 equal parts. On my laptop it finds all 10 solutions in 0.084 seconds.
n = 0
for s in subset_split([4, 3, 5, 6, 4, 3, 1]*2, 4):
n += 1
print(n, s)
So...no performance guarantees. But this should usually be able to find splits pretty quickly per split. Of course there are also usually an exponential number of splits. For example if you take 16 copies of your list and try to split into 32 groups, it takes about 8 minutes on my laptop to find all 224082 solutions.
If I didn't try to deal with negatives, this could be sped up quite a bit. (Use cheaper comparisons, drop all partial sums that have exceeded target to avoid calculating most of the dynamic programming table.)
And here is the sped up version. For the case with only nonnegative numbers it is about twice as fast. If there are negative numbers it will produce wrong results.
from collections import namedtuple
class RecursiveSums (
namedtuple('BaseRecursiveSums',
['value', 'repeat', 'skip', 'tail', 'prev'])):
def sum_and_rest(self):
if self.tail is None:
if self.skip:
yield ([self.value] * self.repeat, [(self.value, self.skip)])
else:
yield ([self.value] * self.repeat, [])
else:
for partial_sum, rest in self.tail.sum_and_rest():
for _ in range(self.repeat):
partial_sum.append(self.value)
if self.skip:
rest.append((self.value, self.skip))
yield (partial_sum, rest)
if self.prev is not None:
yield from self.prev.sum_and_rest()
def lexically_maximal_subset_rest (elements, target, bound=None):
"""
elements = [(value, count), (value, count), ...]
with largest absolute values first.
target = target sum
bound = a lexical bound on the maximal subset.
"""
# First let's deal with all of the trivial cases.
if 0 == len(elements):
if 0 == target:
yield []
elif bound is None or 0 == len(bound):
# Set the bound to something that trivially works.
yield from lexically_maximal_subset_rest(elements, target, [abs(elements[0][0]) + 1])
elif bound[0] < elements[0][0]:
pass # we automatically use more than the bound.
else:
# The trivial checks are done.
bound_satisfied = (bound[0] != elements[0][0])
# recurse_by_sum will have a key of (partial_sum, bound_index).
# If the bound_index is None, the bound is satisfied.
# Otherwise it will be the last used index in the bound.
recurse_by_sum = {}
# Populate it with all of the ways to use the first element at least once.
(init_value, init_count) = elements[0]
for i in range(init_count):
if not bound_satisfied:
if len(bound) <= i or bound[i] < init_value:
# Bound exceeded.
break
elif init_value < bound[i]:
bound_satisfied = True
if bound_satisfied:
key = (init_value * (i+1), None)
else:
key = (init_value * (i+1), i)
recurse_by_sum[key] = RecursiveSums(
init_value, i+1, init_count-i-1, None, recurse_by_sum.get(key))
# And now we do the dynamic programming thing.
for j in range(1, len(elements)):
value, repeat = elements[j]
next_recurse_by_sum = {}
for key, tail in recurse_by_sum.items():
partial_sum, bound_index = key
# Record not using this value at all.
next_recurse_by_sum[key] = RecursiveSums(
value, 0, repeat, tail, next_recurse_by_sum.get(key))
# Now record the rest.
for i in range(1, repeat+1):
if target < partial_sum + value * i:
break # these are too big.
if bound_index is not None:
# Bounds check.
if len(bound) <= bound_index + i:
break # bound exceeded.
elif bound[bound_index + i] < value:
break # bound exceeded.
elif value < bound[bound_index + i]:
bound_index = None # bound satisfied!
if bound_index is None:
next_key = (partial_sum + value * i, None)
else:
next_key = (partial_sum + value * i, bound_index + i)
next_recurse_by_sum[next_key] = RecursiveSums(
value, i, repeat - i, tail, next_recurse_by_sum.get(next_key))
recurse_by_sum = next_recurse_by_sum
# We now have all of the answers in recurse_by_sum, but in several keys.
# Find all that may have answers.
bound_index = len(bound)
while 0 < bound_index:
bound_index -= 1
if (target, bound_index) in recurse_by_sum:
yield from recurse_by_sum[(target, bound_index)].sum_and_rest()
if (target, None) in recurse_by_sum:
yield from recurse_by_sum[(target, None)].sum_and_rest()
def elements_split (elements, target, k, bound=None):
if 0 == len(elements):
if k == 0:
yield []
elif k == 0:
pass # still have elements left over.
else:
for (subset, rest) in lexically_maximal_subset_rest(elements, target, bound):
for answer in elements_split(rest, target, k-1, subset):
answer.append(subset)
yield answer
def subset_split (raw_elements, k):
total = sum(raw_elements)
if 0 == (total % k):
target = total // k
counts = {}
for e in sorted(raw_elements, key=AbsComparator, reverse=True):
counts[e] = 1 + counts.get(e, 0)
elements = list(counts.items())
yield from elements_split(elements, target, k)
n = 0
for s in subset_split([4, 3, 5, 6, 4, 3, 1]*16, 32):
n += 1
print(n, s)
This has a large number of potential solutions so, reducing the number of eligible patterns to evaluate will be key to improving performance.
here's an idea: Approach it in two steps:
generate a list of indexes groups that add up to the target equal sum.
combine the index groups that don't intersect (so indexes are only in one group) so that you get K groups.
The assemble function is a recursive generator that will produce lists of n index combinations (sets) that don't overlap. given that each group has a sum of total/K, the lists will have full coverage of the original lists elements.
def assemble(combos,n):
if not n:
yield []
return
if len(combos)<n: return
for i,idx in enumerate(combos):
others = [c for c in combos if c.isdisjoint(idx)]
for rest in assemble(others,n-1):
yield [idx] + rest
def equalSplit(A,K):
total = sum(A)
if total%K: return # no equal sum solution
partSum = total//K # sum of each resulting sub-lists
combos = [ (0,[]) ] # groups of indices that form sums <= partSum
for i,n in enumerate(A): # build the list of sum,patterns
combos += [ (tot+n,idx+[i]) for tot,idx in combos
if tot+n <= partSum]
# only keep index sets that add up to the target sum
combos = [set(idx) for tot,idx in combos if tot == partSum]
# ouput assembled lists of K sets that don't overlap (full coverage)
seen = set()
for parts in assemble(combos,K):
sol = tuple(sorted(tuple(sorted(A[i] for i in idx)) for idx in parts))
if sol in seen: continue # skip duplicate solutions
yield list(sol)
seen.add(sol)
Output:
A = [4, 3, 5, 6, 4, 3, 1]
print(*equalSplit(A,2), sep='\n')
# [(1, 3, 4, 5), (3, 4, 6)]
# [(1, 3, 3, 6), (4, 4, 5)]
A = [21,22,27,14,15,16,17,18,19,10,11,12,13]
print(*equalSplit(A,5), sep='\n')
# [(10, 15, 18), (11, 13, 19), (12, 14, 17), (16, 27), (21, 22)]
# [(10, 14, 19), (11, 15, 17), (12, 13, 18), (16, 27), (21, 22)]
This will still take a long time for large lists that are split in few parts but it should be a bit better than brute force over combinations

Jump Game II Leetcode, why is my memoization failing?

Here is the problem:
Jump Game II
Given an array of non-negative integers nums, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that
position.
Your goal is to reach the last index in the minimum number of jumps.
You can assume that you can always reach the last index.
class Solution:
def jump(self, nums: List[int]) -> int:
memo = {}
result = self.helper(0, nums, 0, memo)
return result
def helper(self, numJump, nums, currentInd, memo):
if currentInd in memo:
return memo[currentInd]
if currentInd == len(nums) - 1:
return numJump
val = nums[currentInd]
totalMin = float("inf")
for ind in range(1, val + 1):
newInd = currentInd + ind
if newInd >= len(nums):
continue
ret = self.helper(numJump + 1, nums, newInd, memo)
if ret < totalMin:
totalMin = ret
if currentInd not in memo:
memo[currentInd] = totalMin
return totalMin
My solution works without my cache. But as soon as I add it, I get incorrect input.
Here is an example:
input = [1,2,1,1,1]
expected output = 3
actual output = 4
The problem is that you memoize the total steps from the beginning to the current index before all alternatives have been considered. After finding a first path to the end, we know that these distances might not be optimal yet, but still you store them in memo. As you then take an alternative route to get to the same point, you trust that memo to give you the optimal distance -- which is a wrong assumption.
The right way to memoize, is to store the number of steps ahead, as if the current index is the starting point. As this value is only stored when all alternatives starting at that index have been considered, this is an optimal value.
As you backtrack, add 1 step to what you get from the recursive call.
Here is your code adapted with that approach:
def helper(self, nums, currentInd, memo):
if currentInd in memo:
return memo[currentInd]
if currentInd == len(nums) - 1:
return 0 # No more steps needed
val = nums[currentInd]
totalMin = float("inf")
for ind in range(1, val + 1):
newInd = currentInd + ind
if newInd >= len(nums):
break # No need to continue...
ret = self.helper(nums, newInd, memo)
if ret < totalMin:
totalMin = ret
memo[currentInd] = 1 + totalMin # Add step taken
return memo[currentInd]

Find the largest possible substring with k unique letters. (recursive)

I am trying to find max substring possible with k unique letters. Is there a way i can do it recursive by string partition?
My idea is to partition a string by cutting the last characters and if i find the first substring that contains k unique letters i return it.
For example k = 2, string = "abccd"
abccd ->
abcc, bccd ->
abc,bcc,bcc,ccd -> return bcc
def unique_l(sub, k):
u=0
visited = set()
for ch in sub:
if ch not in visited:
visited.add(ch)
u += 1
if u < k:
return -1
elif u == k:
return 1
else:
return 0
def find_sub(string,k):
if unique_l(string,k) == 1:
return string
if unique_l(string,k) == -1:
return "Not Found"
find_sub(string[0:len(string)-1],k) # Left
find_sub(string[1:len(string)],k) # Right
I know that i can do it in O(n) time using iteration but is there a way to do it recursive?
You can use recursion with a generator:
from collections import Counter
def group(d, k):
for i in range(len(d)):
for b in range(i, len(d)):
if len(set((_r:=d[i:b]))) == k:
yield _r
yield from group(_r, k)
r = max(group("abccd", 2), key=len)
Output:
'bcc'

Divide and Conquer. Find the majority of element in array

I am working on a python algorithm to find the most frequent element in the list.
def GetFrequency(a, element):
return sum([1 for x in a if x == element])
def GetMajorityElement(a):
n = len(a)
if n == 1:
return a[0]
k = n // 2
elemlsub = GetMajorityElement(a[:k])
elemrsub = GetMajorityElement(a[k:])
if elemlsub == elemrsub:
return elemlsub
lcount = GetFrequency(a, elemlsub)
rcount = GetFrequency(a, elemrsub)
if lcount > k:
return elemlsub
elif rcount > k:
return elemrsub
else:
return None
I tried some test cases. Some of them are passed, but some of them fails.
For example, [1,2,1,3,4] this should return 1, buit I get None.
The implementation follows the pseudocode here:
http://users.eecs.northwestern.edu/~dda902/336/hw4-sol.pdf
The pseudocode finds the majority item and needs to be at least half. I only want to find the majority item.
Can I get some help?
Thanks!
I wrote an iterative version instead of the recursive one you're using in case you wanted something similar.
def GetFrequency(array):
majority = int(len(array)/2)
result_dict = {}
while array:
array_item = array.pop()
if result_dict.get(array_item):
result_dict[array_item] += 1
else:
result_dict[array_item] = 1
if result_dict[array_item] > majority:
return array_item
return max(result_dict, key=result_dict.get)
This will iterate through the array and return the value as soon as one hits more than 50% of the total (being a majority). Otherwise it goes through the entire array and returns the value with the greatest frequency.
def majority_element(a):
return max([(a.count(elem), elem) for elem in set(a)])[1]
EDIT
If there is a tie, the biggest value is returned. E.g: a = [1,1,2,2] returns 2. Might not be what you want but that could be changed.
EDIT 2
The pseudocode you gave divided into arrays 1 to k included, k + 1 to n. Your code does 1 to k - 1, k to end, not sure if it changes much though ? If you want to respect the algorithm you gave, you should do:
elemlsub = GetMajorityElement(a[:k+1]) # this slice is indices 0 to k
elemrsub = GetMajorityElement(a[k+1:]) # this one is k + 1 to n.
Also still according to your provided pseudocode, lcount and rcount should be compared to k + 1, not k:
if lcount > k + 1:
return elemlsub
elif rcount > k + 1:
return elemrsub
else:
return None
EDIT 3
Some people in the comments highligted that provided pseudocode solves not for the most frequent, but for the item which is present more that 50% of occurences. So indeed your output for your example is correct. There is a good chance that your code already works as is.
EDIT 4
If you want to return None when there is a tie, I suggest this:
def majority_element(a):
n = len(a)
if n == 1:
return a[0]
if n == 0:
return None
sorted_counts = sorted([(a.count(elem), elem) for elem in set(a)], key=lambda x: x[0])
if len(sorted_counts) > 1 and sorted_counts[-1][0] == sorted_counts[-2][0]:
return None
return sorted_counts[-1][1]

Subarray Sum Equals K

Given an array of integers and an integer k, you need to find the total number of continuous subarrays whose sum equals to k.
Trying to solve this problem off of leetcode and not sure exactly where I'm stuck and how to get to the correct solution. It fails for the case nums = [0,0,0,0,0,0,0,0,0,0] and k = 0, the answer I get is 10 when it should really be 55
class Solution(object):
def subarraySum(self, nums, k):
count = 0
for i in range(0, len(nums)):
count += self.driver(nums, k, nums[i], i+1)
return count
def driver(self, nums, target, curr, index):
if curr == target:
return 1
else:
for i in range(index, len(nums)):
return self.driver(nums, target, curr + nums[i], i+1)
return 0
This would be easier with the accumulate function from itertools.
from itertools import accumulate
def subarraySum(a,k):
return sum( list(accumulate(a[i:])).count(k) for i in range(len(a)) )
print(subarraySum([0,0,0,0,0,0,0,0,0,0],0)) # 55
Just let accumulate() do the cumulative sum from each position in the array up to the end and count the number of times it arrives at the desired value. This will cover the case of zero as well as negative numbers.
if you're not allowed to use an imported module, you could write you own accumulate iterator:
def accumulate(a):
s = 0
for v in a: s+=v; yield s

Categories