Building max heap using python i'm encountered with wrong output - python

i'm trying to build max heap in python i have done but after heapify the list output is not satisfy the max heap property.
Can any one help to fix this issue
def max_heapify(arr,i):
left = 2 *i
right = 2 * i + 1
length = len(arr)-1
largest = i
if length > left and arr[largest] < arr[left]:
largest = left
if length > right and arr[largest] < arr[right]:
largest = right
if largest != i:
arr[largest],arr[i] = arr[i],arr[largest]
max_heapify(arr,largest)
def build_max_heap(arr):
for i in reversed(range(len(arr)//2)):
max_heapify(arr,i)
return arr
arr = [1,12,9,5,6,10]
print(build_max_heap(arr))
i'm getting out put [12, 9, 6, 5, 1, 10] which is not satisfy max heap property

There are two issues:
First, the heap is 0-based (the root is at index zero), so the children of node 0 will be 1 and 2, and hence the children of node i will be 2*i+1 and 2*i+2
Your code compared the two children before swapping, but it has not compared the larger child with the parent, and it should only swap if the child is larger than the parent
def max_heapify(arr,i):
left = 2 *i + 1
right = 2 * i + 2
length = len(arr)
largest = i
if length > left and arr[largest] < arr[left]:
largest = left
if length > right and arr[largest] < arr[right]:
largest = right
if arr[largest] > arr[i]:
arr[largest], arr[i] = arr[i],arr[largest]
max_heapify(arr,largest)
def build_max_heap(arr):
N = len(arr)
for i in reversed(range(len(arr)//2)):
max_heapify(arr,i)
return arr
arr = [1,12,9,5,6,10]
print(build_max_heap(arr))
arr = [1,12,9,5,6,10,13]
print(build_max_heap(arr))
output:
[12, 6, 10, 5, 1, 9]
[13, 12, 10, 5, 6, 1, 9]

Related

Max Heapify code.Starting index 1 instead of 0 in python?

def max_heapify(arr, n, i):
largest = i
left = 2*i
right = 2*i + 1
while left <= n and arr[left] > arr[largest]:
largest = left
while right <= n and arr[right] > arr[largest]:
largest = right
if largest != i:
arr[largest], arr[i] = arr[i], arr[largest]
print(arr)
max_heapify(arr, n, i)
arr=[9, 6, 5, 0, 8, 2, 7, 1,3]
n = len(arr)
i = int(n/2)
max_heapify(arr, n, i)
The above Max Heapify code is failing because it' starting from Array 0. How can we change it 1?
for an array starting from 0, you will need to compare left < n and right < n in addition, your initial left and right should be left = 2 * i + 1 and right = 2 * i + 2. To retrieve the max heap, you will also need to call the max_heapify from the first parent node all the way down to 0. Finally there is no need for a while loop when you are assured of it happening only once. Here is a working code that returns the results you require
def max_heapify(arr, n, i):
left = 2*i + 1
right = 2*i + 2
if left < n and arr[left] > arr[i]:
largest = left
else:
largest = i
if right < n and arr[right] > arr[largest]:
largest = right
if largest != i:
arr[largest], arr[i] = arr[i], arr[largest]
print(arr)
max_heapify(arr, n, largest)
if __name__ == "__main__":
arr=[9, 6, 5, 0, 8, 2, 7, 1,3]
n = len(arr)
i = int((n-1)/2)
for b in reversed(range(0,i)):
max_heapify(arr, n, b)

Convert recursive peak finding to iterative

A peak in an array is any value that is no smaller than its two adjacent neighbors. If its the first or last element of the array we only need to compare with one neighbor. I wrote some recursive code to find peaks in python which is fast in principle as it runs in O(log n) time:
def peak_recursive(A):
n = len(A)
if n == 1:
print("n == 1")
return 0
if n == 2:
return 0 if A[0] >= A[1] else 1
if A[n//2] >= A[n//2+1] and A[n//2] >= A[n//2 - 1]:
return n//2
elif A[n//2 - 1] >= A[n//2]:
return peak_recursive(A[0:n//2])
else:
return n//2 + 1 + peak_recursive(A[n//2+1:])
However, python isn't very good at recursion so I think it would be better iteratively. How can I convert this to iterative code?
Update
It turns out this code is very slow as A[n//2+1:] and A[0:n//2] make copies of the lists.
One simple solution is to iterate over the list and compare the previous and next values. You also need to consider the first element and last element situation:
# Import numpy to create random vector
import numpy as np
l = np.random.randint(0, 10, 20).tolist()
print(l)
# [6, 7, 2, 7, 1, 4, 2, 8, 9, 1, 3, 7, 0, 5, 4, 6, 9, 0, 5, 7]
def peak_iter(A):
out = [] # Output
n = len(A) # Number element A
for i, curr in enumerate(A): # Iterate over A
condi = True # Peak condition
if i > 0: condi = A[i-1] < curr # Update peak condition from previous value
if i < n - 1: condi = curr > A[i + 1] # Update peak condition from next value
if condi: out.append(curr) # If condition satisfied: add value to output
return out
print(peak_iter(l))
# [7, 7, 4, 9, 7, 5, 9, 7]
As well, you can easily get the index instead of the value (or the both) by replacing out.append(curr) with out.append(i) or out.append([curr, i]).
Update:
If you just want to get the one peak, you can exit the function after finding one element meeting condition. The following returns the first values:
def peak_iter_first(A):
out = None # Output
n = len(A) # Number element A
for i, curr in enumerate(A): # Iterate over A
condi = True # Peak condition
if i > 0: condi = A[i-1] < curr # Update peak condition from previous value
if i < n - 1: condi = curr > A[i + 1] # Update peak condition from next value
if condi: return curr # If condition satisfied: return value
return out
print(peak_iter_first(l))
# 7
Update 2:
The translation of the recursive function to an iterative one might looks something like this:
def peak_iterative(A):
n = len(A)
out = 0
while True:
if n == 1:
out += 0
break
if n == 2:
out += 0 if A[0] >= A[1] else 1
break
if A[n//2] >= A[n//2+1] and A[n//2] >= A[n//2 - 1]:
out += n//2
break
elif A[n//2 - 1] >= A[n//2]:
A = A[0:n//2]
else:
out += n//2 + 1
A = A[n//2+1:]
n = len(A)
return out
Who's the faster ?
The recursive one is a bit faster than the iterative method:
import timeit
import functools
# Bigger array (2000 elements)
l = np.random.randint(0, 10, 2000).tolist()
t = timeit.Timer(functools.partial(peak_recursive, l))
print (t.timeit(50))
# 3.950000000019216e-05
t = timeit.Timer(functools.partial(peak_iterative, l))
print (t.timeit(50))
# 7.049999999986234e-05
Hope that helps !

Finding Maximum non-negative Subarray in python

I've tried to find the sub-array(s) from a given which contain elements of maximum sum than any other sub array.
Below function has parameter as input a and the output needs to be returned. There can be more than one subarray as their maximum sum can be equal. The code did not seem to be working as expected.
def max_sum_subarray(a):
N, sub_sum, max_sum, subArrays = len(a), 0, 0, {}
p,q=0,0 #starting and ending indices of a max sub arr
for i in range(N):
q=i
sub_sum+=a[i]
if(a[i]<0):
q-=1
if(sub_sum>=max_sum):
if(sub_sum>max_sum):
subArrays.clear()
subArrays[sub_sum]=[(p,q)]
else:
subArrays[sub_sum].append((p,q))
sub_sum=0
p=i+1
if(sub_sum>=max_sum):
if(sub_sum>max_sum):
subArrays.clear()
subArrays[sub_sum]=[(p,q)]
else:
subArrays[sub_sum].append((p,q))
return(subArrays[p:q+1])
When I tried to run for input
a=[ 1, 2, 5, -7, 2, 5 ]
Expected output is [1, 2, 5] but it gave [2, 5] instead. Can anyone please post the solution in python?
It seems like you making this harder than necessary. You can just keep track of max array seen to far and the current one you're pushing into -- you don't really need to care about anything else. When you hit a negative (or the end of the array) decide if the current should be the new max:
def maxSub(a):
max_so_far = []
max_sum = 0
cur = []
for n in a:
if n >= 0:
cur.append(n)
else:
cur_sum = sum(cur)
if cur_sum > max_sum:
max_sum = cur_sum
max_so_far = cur
cur = []
return max([max_so_far, cur], key = sum)
a=[ 1, 2, 5, -7, 2, 5 ]
maxSub(a)
# [1, 2, 5]
Of course itertools.groupby makes this a one-liner:
from itertools import groupby
a=[ 1, 2, 5, -7, 2, 5 ]
max([list(g) for k,g in groupby(a, key=lambda x: x>0) if k == True], key=sum)
For the following conditions:
NOTE 1: If there is a tie, then compare with segment’s length and
return segment which has maximum length
NOTE 2: If there is still a tie, then return the segment with minimum
starting index
Here is my working code in python:
def check(max_arr,curr):
if sum(curr) > sum(max_arr):
max_arr = curr
elif sum(curr) == sum(max_arr):
if len(curr) > len(max_arr):
max_arr = curr
elif len(curr) == len(max_arr):
if max_arr and (curr[0] > max_arr[0]):
max_arr = curr
return max_arr
def maxset(A):
curr = []
max_arr = []
for i in A:
if i >= 0:
curr.append(i)
else:
max_arr = check(max_arr,curr)
curr = []
max_arr = check(max_arr,curr)
return max_arr

Find longest sequence that does not contain a certain number

I have an array and I want to find the longest number sequence from the array that does not contain 3:
#array
x=[1,2,3,4,5,6,5,4,3,3,4,5,2,3,7]
I expect as result as following:
[4, 5, 6, 5, 4]
If I were doing this, I would make groups with itertools.groupby and take the longest one:
from itertools import groupby
a = [1,2,3,4,5,6,5,4,3,3,4,5,2,3,7]
groups = [list(g) for k, g in groupby(a, key=lambda x: x!=3) if k]
max(groups, key = len)
# [4, 5, 6, 5, 4]
Of course there are many other way. If you want to manually loop through the list, you can just keep track of the current longest seen:
a = [1,2,3,4,5,6,5,4,3,3,4,5,2,3,7]
cur = []
longest = cur
for n in a:
if n != 3:
cur.append(n)
if len(cur) > len(longest):
longest = cur
else:
cur = []
print(longest) #[4, 5, 6, 5, 4]
If you want to find sequence (specifically subsequence) then taking all elements which are not 3 will be the result.
But it seems you want to find sub array. Following is my implementation for the same in python
def findLargestSubArray(arr, k):
# collect all the index position of k in arr
kPos = [i for i in range(len(arr)) if arr[i] == k]
largest = 0
left = -1
right = -1
# size of subarray to the left of 1st instance of k
if len(kPos) > 0:
largest = kPos[0] - 1
left = 0
right = kPos[0]
for i in range(1, len(kPos)):
# size of subarray between ith & (i-1)th instance of k
currSize = kPos[i] - kPos[i-1] - 1
if largest < currSize:
largest = currSize
left = kPos[i-1] + 1
right = kPos[i]
# size of subarry to the right of last instance of k
if largest < len(arr) - kPos[-1] - 1:
largest = len(arr) - kPos[-1] - 1
left = kPos[-1] + 1
right = len(arr)
return arr[left: right]
x = [3,3]
print(findLargestSubArray(x, 3))

finding contiguous Subset with Largest Sum

def max_sublist(x):
max1 = 0
max2 = 0
result = []
for i in x:
max2 = max(0, max2 + i)
max1 = max(max1, max2)
print result
I want to add elements till the element which had the max sum. How do I add only whose elements to the result.
For ex. if x = [4, -1, 5, 6, -13, 2]
then result should be [4, -1, 5, 6]
This is a classic problem in optimization, and it's called the maximum subarray problem. Here's one possible dynamic programming solution in O(n), using Kadane's algorithm:
def max_val_contiguous_subsequence_idxs(seq):
i = thisSum = maxSum = 0
startIdx, endIdx = 0, -1
for j in xrange(len(seq)):
thisSum += seq[j]
if thisSum > maxSum:
maxSum = thisSum
startIdx = i
endIdx = j
elif thisSum < 0:
thisSum = 0
i = j + 1
return (maxSum, startIdx, endIdx)
The above will return in a single pass a tuple with the maximum sum, the starting index and the end index of the subsequence. For example, using the sample input in the question:
lst = [4, -1, 5, 6, -13, 2]
maxSum, startIdx, endIdx = max_val_contiguous_subsequence_idxs(lst)
maxSum
=> 14
lst[startIdx:endIdx+1]
=> [4, -1, 5, 6]
Notice that the implementations shown in the wikipedia page (which look a lot like the solution you were aiming for) only give the maximum sum, but unlike my solution they don't tell you how to find the subsequence indexes in the array.

Categories

Resources