Jump Game II Leetcode, why is my memoization failing? - python

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]

Related

How can I return the index of mid in my recursive binary search program?

class Solution:
def binarysearch(self, arr, n, k):
# code here
first = 0
last = n - 1
mid = (first + last)//2
if(arr[mid]==k):
//Here
elif(k > arr[mid]):
self.binarysearch(arr[mid+1:n], len(arr[mid+1:n]), k)
elif(k < arr[mid]):
self.binarysearch(arr[0:mid], len(arr[0: mid]), k)
else:
return -1
I can't figure out how to get the index of mid as I am manipulating the array in every call. The parameters of the function are fixed, I can't give the first and last parameters to the function. I tried using the iterative approach and I got it but I am not able to do it like this. I just want the logic to get the index of mid in the code.
To get index of middle element in array you can pass an additional parameter(eg. start_index in below code) to function that keeps track of the starting index of current subarray
class Solution:
def binarysearch(self, arr, n, k, start_index):
# code here
first = 0
last = n - 1
mid = (first + last)//2
if(arr[mid]==k):
return start_index + mid # Return the index of mid in the original array
elif(k > arr[mid]):
return self.binarysearch(arr[mid+1:n], len(arr[mid+1:n]), k, start_index + mid + 1)
elif(k < arr[mid]):
return self.binarysearch(arr[0:mid], len(arr[0: mid]), k, start_index)
else:
return -1

Leetcode question '3Sum' algorithm exceeds time limit, looking for improvement

Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
class Solution:
def threeSum(self, nums):
data = []
i = j = k =0
length = len(nums)
for i in range(length):
for j in range(length):
if j == i:
continue
for k in range(length):
if k == j or k == i:
continue
sorted_num = sorted([nums[i],nums[j],nums[k]])
if nums[i]+nums[j]+nums[k] == 0 and sorted_num not in data:
data.append(sorted_num)
return data
My soulution is working well but it appears that it may be too slow.
Is there a way to improve my codes without changing it significantly?
This is a O(n^2) solution with some optimization tricks:
import itertools
class Solution:
def findsum(self, lookup: dict, target: int):
for u in lookup:
v = target - u
# reduce duplication, we may enforce v <= u
try:
m = lookup[v]
if u != v or m > 1:
yield u, v
except KeyError:
pass
def threeSum(self, nums: List[int]) -> List[List[int]]:
lookup = {}
triplets = set()
for x in nums:
for y, z in self.findsum(lookup, -x):
triplets.add(tuple(sorted([x, y, z])))
lookup[x] = lookup.get(x, 0) + 1
return [list(triplet) for triplet in triplets]
First, you need a hash lookup to reduce your O(n^3) algorithm to O(n^2). This is the whole idea, and the rest are micro-optimizations:
the lookup table is build along with the scan on the array, so it is one-pass
the lookup table index on the unique items that seen before, so it handles duplicates efficiently, and by using that, we keep the iteration count of the second-level loop to the minimal
This is an optimized version, will pass through:
from typing import List
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
unique_triplets = []
nums.sort()
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i - 1]:
continue
lo = i + 1
hi = len(nums) - 1
while lo < hi:
target_sum = nums[i] + nums[lo] + nums[hi]
if target_sum < 0:
lo += 1
if target_sum > 0:
hi -= 1
if target_sum == 0:
unique_triplets.append((nums[i], nums[lo], nums[hi]))
while lo < hi and nums[lo] == nums[lo + 1]:
lo += 1
while lo < hi and nums[hi] == nums[hi - 1]:
hi -= 1
lo += 1
hi -= 1
return unique_triplets
The TLE is most likely for those instances that fall into these two whiles:
while lo < hi and nums[lo] == nums[lo + 1]:
while lo < hi and nums[lo] == nums[lo + 1]:
References
For additional details, please see the Discussion Board where you can find plenty of well-explained accepted solutions with a variety of languages including low-complexity algorithms and asymptotic runtime/memory analysis1, 2.
I'd suggest:
for j in range(i+1, length):
This will save you len(nums)^2/2 steps and first if statement becomes redundant.
sorted_num = sorted([nums[i],nums[j],nums[k]])
if nums[i]+nums[j]+nums[k] == 0 and sorted_num not in data:
sorted_num = sorted([nums[i],nums[j],nums[k]])
data.append(sorted_num)
To avoid unneeded calls to sorted in the innermost loop.
Your solution is the brute force one, and the slowest one.
Better solutions can be:
Assume you start from an element from array. Consider using a Set for finding next two numbers from remaining array.
There is a 3rd better solution as well. See https://www.gyanblog.com/gyan/coding-interview/leetcode-three-sum/

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]

python3 binary search not working [duplicate]

I am trying to implement the binary search in python and have written it as follows. However, I can't make it stop whenever needle_element is larger than the largest element in the array.
Can you help? Thanks.
def binary_search(array, needle_element):
mid = (len(array)) / 2
if not len(array):
raise "Error"
if needle_element == array[mid]:
return mid
elif needle_element > array[mid]:
return mid + binary_search(array[mid:],needle_element)
elif needle_element < array[mid]:
return binary_search(array[:mid],needle_element)
else:
raise "Error"
It would be much better to work with a lower and upper indexes as Lasse V. Karlsen was suggesting in a comment to the question.
This would be the code:
def binary_search(array, target):
lower = 0
upper = len(array)
while lower < upper: # use < instead of <=
x = lower + (upper - lower) // 2
val = array[x]
if target == val:
return x
elif target > val:
if lower == x: # these two are the actual lines
break # you're looking for
lower = x
elif target < val:
upper = x
lower < upper will stop once you have reached the smaller number (from the left side)
if lower == x: break will stop once you've reached the higher number (from the right side)
Example:
>>> binary_search([1,5,8,10], 5) # return 1
1
>>> binary_search([1,5,8,10], 0) # return None
>>> binary_search([1,5,8,10], 15) # return None
Why not use the bisect module? It should do the job you need---less code for you to maintain and test.
array[mid:] creates a new sub-copy everytime you call it = slow. Also you use recursion, which in Python is slow, too.
Try this:
def binarysearch(sequence, value):
lo, hi = 0, len(sequence) - 1
while lo <= hi:
mid = (lo + hi) // 2
if sequence[mid] < value:
lo = mid + 1
elif value < sequence[mid]:
hi = mid - 1
else:
return mid
return None
In the case that needle_element > array[mid], you currently pass array[mid:] to the recursive call. But you know that array[mid] is too small, so you can pass array[mid+1:] instead (and adjust the returned index accordingly).
If the needle is larger than all the elements in the array, doing it this way will eventually give you an empty array, and an error will be raised as expected.
Note: Creating a sub-array each time will result in bad performance for large arrays. It's better to pass in the bounds of the array instead.
You can improve your algorithm as the others suggested, but let's first look at why it doesn't work:
You're getting stuck in a loop because if needle_element > array[mid], you're including element mid in the bisected array you search next. So if needle is not in the array, you'll eventually be searching an array of length one forever. Pass array[mid+1:] instead (it's legal even if mid+1 is not a valid index), and you'll eventually call your function with an array of length zero. So len(array) == 0 means "not found", not an error. Handle it appropriately.
This is a tail recursive solution, I think this is cleaner than copying partial arrays and then keeping track of the indexes for returning:
def binarySearch(elem, arr):
# return the index at which elem lies, or return false
# if elem is not found
# pre: array must be sorted
return binarySearchHelper(elem, arr, 0, len(arr) - 1)
def binarySearchHelper(elem, arr, start, end):
if start > end:
return False
mid = (start + end)//2
if arr[mid] == elem:
return mid
elif arr[mid] > elem:
# recurse to the left of mid
return binarySearchHelper(elem, arr, start, mid - 1)
else:
# recurse to the right of mid
return binarySearchHelper(elem, arr, mid + 1, end)
def binary_search(array, target):
low = 0
mid = len(array) / 2
upper = len(array)
if len(array) == 1:
if array[0] == target:
print target
return array[0]
else:
return False
if target == array[mid]:
print array[mid]
return mid
else:
if mid > low:
arrayl = array[0:mid]
binary_search(arrayl, target)
if upper > mid:
arrayu = array[mid:len(array)]
binary_search(arrayu, target)
if __name__ == "__main__":
a = [3,2,9,8,4,1,9,6,5,9,7]
binary_search(a,9)
Using Recursion:
def binarySearch(arr,item):
c = len(arr)//2
if item > arr[c]:
ans = binarySearch(arr[c+1:],item)
if ans:
return binarySearch(arr[c+1],item)+c+1
elif item < arr[c]:
return binarySearch(arr[:c],item)
else:
return c
binarySearch([1,5,8,10,20,50,60],10)
All the answers above are true , but I think it would help to share my code
def binary_search(number):
numbers_list = range(20, 100)
i = 0
j = len(numbers_list)
while i < j:
middle = int((i + j) / 2)
if number > numbers_list[middle]:
i = middle + 1
else:
j = middle
return 'the index is '+str(i)
If you're doing a binary search, I'm guessing the array is sorted. If that is true you should be able to compare the last element in the array to the needle_element. As octopus says, this can be done before the search begins.
You can just check to see that needle_element is in the bounds of the array before starting at all. This will make it more efficient also, since you won't have to do several steps to get to the end.
if needle_element < array[0] or needle_element > array[-1]:
# do something, raise error perhaps?
It returns the index of key in array by using recursive.
round() is a function convert float to integer and make code fast and goes to expected case[O(logn)].
A=[1,2,3,4,5,6,7,8,9,10]
low = 0
hi = len(A)
v=3
def BS(A,low,hi,v):
mid = round((hi+low)/2.0)
if v == mid:
print ("You have found dude!" + " " + "Index of v is ", A.index(v))
elif v < mid:
print ("Item is smaller than mid")
hi = mid-1
BS(A,low,hi,v)
else :
print ("Item is greater than mid")
low = mid + 1
BS(A,low,hi,v)
BS(A,low,hi,v)
Without the lower/upper indexes this should also do:
def exists_element(element, array):
if not array:
yield False
mid = len(array) // 2
if element == array[mid]:
yield True
elif element < array[mid]:
yield from exists_element(element, array[:mid])
else:
yield from exists_element(element, array[mid + 1:])
Returning a boolean if the value is in the list.
Capture the first and last index of the list, loop and divide the list capturing the mid value.
In each loop will do the same, then compare if value input is equal to mid value.
def binarySearch(array, value):
array = sorted(array)
first = 0
last = len(array) - 1
while first <= last:
midIndex = (first + last) // 2
midValue = array[midIndex]
if value == midValue:
return True
if value < midValue:
last = midIndex - 1
if value > midValue:
first = midIndex + 1
return False

Python function to split and add lists recursively

I'm having trouble writing a function in python that will take a list, split it into 2 equal sides and then recursively add each element in each half. In the end returning the sum of both halves.
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 1:
return aList[0]
else:
sumOfLeft = sumLists(leftHalf[1:])
resultLeft = leftHalf[0] + sumOfLeft
sumOfRight = sumLists(rightHalf[1:])
resultRight = rightHalf[0] + sumOfRight
return resultLeft + resultRight
Any tips are appreciated, thanks!
You're overcomplicating the else block. You don't need to call sumLists on leftHalf[1:] and rightHalf[1:] and manually add the first respective elements; it suffices to call sumLists on the complete lists.
This slicing is what's causing your RuntimeError. A leftHalf with length one will have a leftHalf[1:] of length zero. But your function recurses forever for lengths of length zero because you didn't write an if case for that scenario.
You could rewrite your else so that it doesn't require slicing:
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 1:
return aList[0]
else:
return sumLists(leftHalf) + sumLists(rightHalf)
... Or you could add a special case for empty lists:
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 0:
return 0
elif len(aList) == 1:
return aList[0]
else:
sumOfLeft = sumLists(leftHalf[1:])
resultLeft = leftHalf[0] + sumOfLeft
sumOfRight = sumLists(rightHalf[1:])
resultRight = rightHalf[0] + sumOfRight
return resultLeft + resultRight
Or both:
def sumLists(aList):
half = len(aList)//2
leftHalf = aList[half:]
rightHalf = aList[:half]
if len(aList) == 0:
return 0
if len(aList) == 1:
return aList[0]
else:
return sumLists(leftHalf) + sumLists(rightHalf)
I think aList[half:] is the right side, and aList[:half] is the left side, is it right?
the follow code will sum the list left and right side, hope this can solve your problem.
def sumlist(l):
if not l or not isinstance(l, list):
return 0 # or return other default value.
if len(l) == 1:
return l[0]
half = len(l) // 2
left = l[:half] # left
right = l[-half:] # right
return sumlist(left) + sumlist(right)
test:
l = [1,2,3,4,5,6,7,8,9]
result = sumlist(l)
print(result) # 40
Maybe I misread your question, but do you actually need to do both things in one function?
To sum a list recursively, you could define that the empty list (your base case) has the sum 0. Any non-empty list has the sum of the first element plus the sum of the remainder (the "tail") of the list:
def sumList(a):
return 0 if not a else a[0] + sumList(a[1:])
To split a list recursively, you could keep track of the "left" and "right" list. The left list starts out being your input, the right list is empty. You then recursively prepend the last element of the left list to the right list until the right list is no longer shorter than the left:
def splitList(a, b=None):
b = b or []
return (a, b) if len(a) <= len(b) else splitList(a[:-1], [a[-1]] + b)
I suspect that passing around slices of lists is very inefficient in Python, so it might be better to rather pass around indices, e.g.
def sumList(a, idx=None):
idx = idx or 0
return 0 if idx >= len(a) else a[idx] + sumList(a, idx+1)

Categories