How to print nodes in heap recursively - python

Given heap as an array with keys , how to print all keys ? So far I got
def heapPrint (arr,i):
if (len(arr) == 0 or len(arr) == 1):
return
if (left(i) > len(arr) or right(i) > len(arr)):
return
if (left(i) < len(arr) and right(i) < len(arr):
print (arr[left(i)],arr[right(i)])
heapPrint (arr,left(i))
heapPrint (arr,right(i))
Isnt there any better solution where I could print just 1 node and not 2? Thanks

I think you're looking for something like this:
def heapPrint(arr, i)
# if the index is larger than the heap length
if i >= len(arr):
return;
# if it's a leaf node, then return
if left(i) >= len(arr) and right(i) >= len(arr):
return
if left(i) < len(arr):
print(arr[left(i)], end=' ')
if right(i) < len(arr):
print(arr[right(i)], end='')
print('')
# these calls will return immediately if the
# index is too large.
heapPrint(arr, left(i))
heapPrint(arr, right(i))
My python skills are minimal, so there might be some syntax errors there, but that's the basic idea.

Related

Describing QuickSort Algoritm

Im having a problem to understand this algorithm, how would you describe it, mostly the while loops. I understand that this i an iterative function, and the use of Hoare partition scheme. But what does the while loops do, and why does it use breaks.
def inplace_quick_sort_nonrecursive(S):
if len(S) < 2:
return S
stack = [] #initialize the stack
stack.append([0, len(S) - 1])
while len(stack) > 0: #loop till the stack is empty
low, high = stack.pop() #pop low and high indexes
pivot = S[(low + high) // 2] #create pivot, of any S[] except S[high]
i = low - 1 #Hoare partition
j = high + 1
while True:
while True: #while (S[++i] < pivot)
i += 1
if(S[i] >= pivot):
break
while True: #while(S[--j] < p)
j -= 1
if(S[j] <= pivot):
break
if (i >= j): #if indexes met or crossed, break
break
S[i], S[j] = S[j], S[i] #else swap the elements
if (j > low): #push the indexes into the stack
stack.append([low, j])
j += 1
if (high > j):
stack.append([j, high])

Problem with finding array's local maximas and their positions Python

So basically I'm trying to complete this assignment: https://www.codewars.com/kata/5279f6fe5ab7f447890006a7
The question: Why does my function count position 11 as a local maxima of this particular array?
Here is my code:
def main():
arr = [1,2,5,4,3,2,3,6,4,1,2,3,3,4,5,3,2,1,2,3,5,5,4,3]
maxes = []
positions = []
dic = {"pos":positions, "peaks":maxes}
for i in range(1,len(arr)-1):
try:
if arr[i] > arr[i+1] and arr[i] > arr[i-1]:
maxes.append(arr[i])
positions.append(i)
elif arr[i] > arr[i-1] and arr[i] == arr[i+1]:
for a in range(i+1,len(arr)):
if arr[a] > arr[a+1]:
maxes.append(arr[i])
positions.append(i)
break
except IndexError:
pass
print(dic)
main()
My output:
{'pos': [2, 7, 11, 14, 20], 'peaks': [5, 6, 3, 5, 5]}
Correct output:
{'pos': [2, 7, 14, 20], 'peaks': [5, 6, 5, 5]}
When you see a situation like "2,3,3, 4,5,3...?" you save the position and run further try to find out: 1) at the end the value is less (then this is the peak and the saved value came in handy), 2) the value is larger (not a peak, plateau, the saved value was not useful).
In you particular situation in second condition:
elif arr[i] > arr[i-1] and arr[i] == arr[i+1]: you begin to iterate from second 3 and stop only when arr[a] > arr[a+1]. In this moment you append you old i value (which indicates first 3):
maxes.append(arr[i]); positions.append(i)
that is, you do not take into account that the value further may be greater and you need to interrupt.
A working example of the function can be seen below:
def main():
arr = [1,2,5,4,3,2,3,6,4,1,2,3,3,4,5,3,2,1,2,3,5,5,4,3]
dic = {'pos' : [], 'peaks' : []}
i, n = 1, len(arr)
while i < n - 1:
if arr[i-1] < arr[i] > arr[i+1]:
dic['peaks'].append(arr[i])
dic['pos'].append(i)
i += 1
else:
if arr[i-1] < arr[i] and arr[i] == arr[i+1]:
mem = i
while i < n -1 and arr[i] == arr[i+1]:
i += 1
if i == n - 1:
return dic
if arr[i] > arr[i+1]:
dic['peaks'].append(arr[mem])
dic['pos'].append(mem)
else:
i += 1
else:
i += 1
return dic
print(main())
When you find a following value that is equal and continue to search for a lesser value you should also break the loop if you find a greater value. It's not a peak if the group of equals are followed by a greater value.
A small addition is enough to make your code work.
def main():
arr = [1,2,5,4,3,2,3,6,4,1,2,3,3,4,5,3,2,1,2,3,5,5,4,3]
maxes = []
positions = []
dic = {"pos":positions, "peaks":maxes}
for i in range(1,len(arr)-1):
try:
if arr[i] > arr[i+1] and arr[i] > arr[i-1]:
maxes.append(arr[i])
positions.append(i)
elif arr[i] > arr[i-1] and arr[i] == arr[i+1]:
for a in range(i+1,len(arr)):
if arr[a] > arr[a+1]:
maxes.append(arr[i])
positions.append(i)
break
# also break if a greater value is found
elif arr[a] < arr[a+1]:
break
except IndexError:
pass
print(dic)
main()
A simpler approach may be to iterate just once and take note of the position every time you find an increase. Then save that position and its value when you find a decrease afterwards.
def pick_peaks(arr):
dic = {"pos":[], "peaks":[]}
possible_peak = None
for i in range(1,len(arr)):
if arr[i] > arr[i-1]:
# Value is going up. Remember position
possible_peak = i
elif arr[i] < arr[i-1] and possible_peak != None:
# It was indeed a peak. Save it.
dic["pos"].append(possible_peak)
dic["peaks"].append(arr[possible_peak])
# reset memory until new increase is found
possible_peak = None
return dic
input_arr = [1,2,5,4,3,2,3,6,4,1,2,3,3,4,5,3,2,1,2,3,5,5,4,3]
print(pick_peaks(input_arr))

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/

Merge sort in python: slicing vs iterating - impact on complexity

I want to check that my understanding of how python handles slices is correct.
Here's my implementation of merge sort:
def merge_sort(L):
def merge(a, b):
i, j = 0, 0
c = []
while i < len(a) and j < len(b):
if a[i] < b[j]:
c.append(a[i])
i += 1
elif b[j] < a[i]:
c.append(b[j])
j += 1
if a[i:]:
c.extend(a[i:])
if b[j:]:
c.extend(b[j:])
return c
if len(L) <= 1:
return L
else:
mid = len(L) // 2
left = merge_sort(L[:mid])
right = merge_sort(L[mid:])
return merge(left, right)
Am I right in thinking that I could replace this:
if a[i:]:
c.extend(a[i:])
if b[j:]:
c.extend(b[j:])
With this:
while i < len(a):
c.append(a[i])
i += 1
while j < len(b):
c.append(b[j])
j += 1
And have the exact same level of complexity? My understanding of slicing is that its complexity is equivalent to slice length? Is that correct?
Does the fact that I'm calling a slice twice (first in the condition, second time inside of it) make it 2x complexity?
Your implementation of mergesort has problems:
in the merge function's main loop, you do nothing if the values in a[i] and b[j] are equal, or more precisely if you have neither a[i] < b[i] nor a[i] > b[i]. This causes an infinite loop.
there is no need to define merge as a local function, actually there is no need to make it a separate function, you could inline the code and save the overhead of a function call.
Here is a modified version:
def merge_sort(L):
if len(L) <= 1:
return L
else:
mid = len(L) // 2
a = merge_sort(L[:mid])
b = merge_sort(L[mid:])
i, j = 0, 0
c = []
while i < len(a) and j < len(b):
if a[i] <= b[j]:
c.append(a[i])
i += 1
else:
c.append(b[j])
j += 1
if a[i:]:
c.extend(a[i:])
else:
c.extend(b[j:])
return c
Regarding performance, slicing or iterating has no impact on complexity since both operations have linear time cost.
Regarding performance, here are directions to try:
replace the test if a[i:] with if i < len(a). Creating the slice twice is costly.
perform the sort in place, avoiding the append operations
restructure the main loop to have a single test per iteration
Here is a modified version:
def merge_sort(L):
if len(L) <= 1:
return L
else:
mid = len(L) // 2
a = merge_sort(L[:mid])
b = merge_sort(L[mid:])
i, j, k = 0, 0, 0
while True:
if a[i] <= b[j]:
L[k] = a[i]
k += 1
i += 1
if (i == len(a)):
L[k:] = b[j:]
return L
else:
L[k] = b[j]
k += 1
j += 1
if (j == len(b)):
L[k:] = a[i:]
return L

Python understanding recursive program

I am trying to understand the following program to find a number of the subset which forms a given sum from a given list.
def count_sets(arr, total):
return rec(arr, total, len(arr)-1)
def rec(arr, total, i):
print(arr, total, i)
if total == 0:
return 1
if i < 0:
return 0
if arr[i] > total:
return rec(arr, total, i-1)
else:
return rec(arr, total-arr[i], i-1) + rec(arr, total, i-1)
arr = [2,10,6,4]
print(count_sets(arr, 16))
the program works without any error, however, I am not able to find how it works.
It is a backtracking algorithm. In recursion rec(arr, total, i), it picks the last element arr[i] in rest array, and here are two main situations:
use arr[i]: rec(arr, total-arr[i], i-1)
not use arr[i]: rec(arr, total, i-1), and when arr[i] > total of course you can only not use it.
and we must have terminate condition for recursion, that is:
[success] find the subarray equal to total: if total == 0: return 1
[failed] reach the head: if i < 0: return 0
Hope I make it clear, and comment if you have further questions. : )

Categories