heaps and priority queue with pseudocode - heap

I have a question at school that i need to find the kth minimum elements in a min heap. the run time that was required is o(k^2) and i understood how to do that. but if i can get it down to o(k*logk) that i get a bonus. i thought about doing a priority queue from the min heap and then inserting the node of the heap to the queue and then taking it out then doing the same thing with the children of the root of the minimum heap and so on k times. i know that the time complexity of insert and pop operations is o(logk) since The initial size of the priority queue is one, and it increases by at most one at each of the k steps. Therefore, there are maximum k+1 elements in the priority queue.
i understand what i need to do but find it complex to implement it in pseudo code any ideas or guidelines would be great.
Thank you

Yes, your idea sounds good. I will show how to do this in Python.
Build heap from array
So, first build a min heap on top of your input array:
def create_min_heap(array):
min_heap = []
for value in array:
heappush(min_heap, value)
return min_heap
Count k min elements off the heap
Create a helper min-heap that will be used to retrieve all k minimum elements in O(klogk). At each step, a minimum element will be popped from it, and its 2 children nodes will be added (children can be found in the original min-heap). Please note, that it doesn't make sense to add children of other nodes to the helper min-heap because they cannot be smaller than their parents (per heap property).
def k_min_elements(min_heap, k):
result = list()
helper_min_heap = []
heappush(helper_min_heap, (min_heap[0],0))
while len(result) < k:
min_node = heappop(helper_min_heap)
value = min_node[0]
index = min_node[1]
left_index = index*2 + 1
right_index = left_index + 1
if left_index < len(min_heap):
heappush(helper_min_heap, (min_heap[left_index], left_index))
if right_index < len(min_heap):
heappush(helper_min_heap, (min_heap[right_index], right_index))
result.append(value)
return result
Full Code
Now, the full code and sample output.
from heapq import heappop
from heapq import heappush
def create_min_heap(array):
min_heap = []
for value in array:
heappush(min_heap, value)
return min_heap
def k_min_elements(min_heap, k):
if k > len(min_heap) or k < 0:
raise Exception("k is invalid")
result = list()
helper_min_heap = []
heappush(helper_min_heap, (min_heap[0],0))
while len(result) < k:
min_node = heappop(helper_min_heap)
value = min_node[0]
index = min_node[1]
left_index = index*2 + 1
right_index = left_index + 1
if left_index < len(min_heap):
heappush(helper_min_heap, (min_heap[left_index], left_index))
if right_index < len(min_heap):
heappush(helper_min_heap, (min_heap[right_index], right_index))
result.append(value)
return result
min_heap = create_min_heap([1, 3, 5, 7, 9, 2, 4, 6, 8, 0])
print (k_min_elements(min_heap, 3))
[0, 1, 2]

Related

Intuition behind storing the remainders?

I am trying to solve a question on LeetCode.com:
Given a list of non-negative numbers and a target integer k, write a function to check if the array has a continuous subarray of size at least 2 that sums up to the multiple of k, that is, sums up to n*k where n is also an integer. For e.g., if [23, 2, 4, 6, 7], k=6, then the output should be True, since [2, 4] is a continuous subarray of size 2 and sums up to 6.
I am trying to understand the following solution:
class Solution {
public:
bool checkSubarraySum(vector<int>& nums, int k) {
int n = nums.size(), sum = 0, pre = 0;
unordered_set<int> modk;
for (int i = 0; i < n; ++i) {
sum += nums[i];
int mod = k == 0 ? sum : sum % k;
if (modk.count(mod)) return true;
modk.insert(pre);
pre = mod;
}
return false;
}
};
I understand that we are trying to store: 0, (a/k), (a+b)/k, (a+b+c)/k, etc. into the hashSet (where k!=0) and that we do that in the next iteration since we want the subarray size to be at least 2.
But, how does this guarantee that we get a subarray whose elements sum up to k? What mathematical property guarantees this?
The set modk is gradually populated with all sums (considered modulo k) of contiguous sub-arrays starting at the beginning of the array.
The key observation is that:
a-b = n*k for some natural n iff
a-b ≡ 0 mod k iff
a ≡ b mod k
so if a contiguous sub-array nums[i_0]..nums[i_1], sums up to 0 modulo k, then the two sub-arrays nums[0]..nums[i_0] and nums[i_0 + 1]..nums[i_1] have the same sum modulo k.
Thus it's enough if two distinct sub-arrays starting at the beginning of the array have the same sum, modulo k.
Luckily, there are only k such values, so you only need to use a set of size k.
Some nitpicks:
if n > k, you're going to have an appropriate sub-array anyway (the pigeon-hole principle), so the loop will actually never iterate more than k+1 times.
There should not be any sort of class involved here, that makes no sense.
contiguous, not continuous. Arrays and sub-arrays are discrete and can't be continuous...
module base k of sum is equivalent to the module k of sum of the modules base k
(a+b)%k = (a%k + b%k) % k
(23 + 2) % 6 = 1
( (23%6) + (2%6) ) % 6 = (5 + 2) % 6 = 1
modk stores all modules that you calculated iteratively. If at iteration i you get a repeated module calculated at i-m that means that you added a subsequence of m elements which sum is multiple of k
i=0 nums[0] = 23 sum = 23 sum%6 = 5 modk = [5]
i=1 nums[1] = 2 sum = 25 sum%6 = 1 modk = [5, 1]
i=2 nums[2] = 4 sum = 29 sum%6 = 5 5 already exists in modk (4+2)%6 =0

Python: Finding Lowest Unique Integer from List

I am trying to find out lowest unique element from list. I have been able to generate O(n^2) and O(n) solutions. But I don't find them optimized. Kindly help me understand,if there is a possible O(n) solution for it. No one liner solutions please. Below are my codes:
Main function:
if __name__ =="__main__":
print uniqueMinimum([6, 2, 6, -6, 45, -6, 6])
print lowestUnique([5, 10, 6, -6, 3, -6, 16])
O(n^2) Solution:
def lowestUnique(arr):
num = max(arr)
for i in range(len(arr)):
check = False
for j in range(len(arr)):
if arr[i]==arr[j] and i!=j:
check =True
if check==False:
if num > arr[i]:
num = arr[i]
return num
I would like to avoid using max(array) in above solution.
O(n) Solution:
def uniqueMinimum(array):
d ={}
a =[]
num = max(array)
for i in range(len(array)):
k =d.get(array[i])
if k is None:
d[array[i]] = 1
a.append(array[i])
else:
d[array[i]] = k+1
if array[i] in a:
a.remove(array[i])
a.sort()
return a[0]
I can't really comment on the big-O since it depends on the implementation of the built-in python functions which I've never looked into. Here's a more reasonable implementation though:
def lowestUnique(array):
for element in sorted(list(set(array))):
if array.count(element) == 1:
return element
raise Exception('No unique elements found.')

Counter for a list adding up to 10

I'm currently stuck on a question regarding lists and formulating codes. I have to
For example, if list = [4,6] I would have to return 2. (4+6) and (6+4). This code has to work for any length of list and no two numbers will be the same within the list. I'm new to lists and and stuck on how to begin coding.
def countsum(list):
Would appreciate the help
def count_list_aux(num_possibilities, a_list, i, j):
if i == j: # Only 1 item to check now.
if a_list[i] == 10: # The item itself is 10 so that can be a combo.
return num_possibilities + 1
else: # Otherwise we are done.
return num_possibilities
else:
combo = a_list[i] + a_list[j]
if combo == 10: # (4,6) for instance.
return count_list_aux(num_possibilities+2, a_list, i+1, j-1)
elif combo > 10: # (4,8) for instance. Clearly 8 needs to go when we have a sorted list.
return count_list_aux(num_possibilities, a_list, i, j-1)
else: # (4,7) for instance. Clearly 4 needs to go when we have a sorted list.
return count_list_aux(num_possibilities, a_list, i+1, j)
def countsum(a_list):
a_list = sorted(a_list)
return count_list_aux(0, a_list, 0, len(a_list)-1)
print(countsum([4,6,3,7,5,2,8]))
I have made a recursive solution where I simply sort the list (ascending order) and then recursively add up left most (i) and right most (j) and check if they add up to 10. If they do, then I increment num_possibiltiies by 2 (eg. 6,4 and 4,6 are 2 combos). If the sum is greater than 10, then I decrement j by 1 because clearly, the current index j cannot work with any other values in the list (list is sorted). Similarly, if the sum is smaller than 10, I increment i by 1 as the current index i cannot work with any other values to get a sum of 10 (it failed with the largest value at index j).
The function can also be implemented using:
from itertools import combinations
from math import factorial
def countsum(L):
x = 10
n = 2
return sum(factorial(n) for c in combinations(L, n) if sum(c) == x)
The factorial is because combinations produces tuples that are sets of items, rather than counting each of the permutations, simply compute their count. As the factorial depends on a constant it can be hoisted:
def countsum(L):
x = 10
n = 2
return factorial(n) * sum(1 for c in combinations(L, n) if sum(c) == x)

Traversing an array to find the second largest element in linear time

Is there a way in linear time by which we can find which is the second largest element of an array ?
Array elements can be positive, negative or zero.
Elements can be repetitive.
No STLs allowed.
Python can be used.
Solution : Sort the array and take the second element but Sorting not allowed
Modification : By definition second largest element will be the one which is numerically smaller. Like if we have
Arr = {5,5,4,3,1}
Then second largest is 4
Addition
Lets say if i want to generalize the question to kth largest and complexity less than linear like nlogn, what can be the solution.
Go through the array, keeping 2 memory slots to record the 2 largest elements seen so far. Return the smaller of the two.
.... is there anything tricky about this question that I can't see?
You can, this is the pseudo algorithm:
max = 2max = SMALLEST_INT_VALUE;
for element in v:
if element > max:
2max = max;
max = element;
else if element > 2max:
2max = element;
2max is the value you are looking for.
The algorithm won't return a correct value for particular cases, such as an array where its elements are equal.
If you want a true O(n) algorithm, and want to find nth largest element in array then you should use quickselect (it's basically quicksort where you throw out the partition that you're not interested in), and the below is a great writeup, with the runtime analysis:
http://pine.cs.yale.edu/pinewiki/QuickSelect
Pseudo code:
int max[2] = { array[0], array[1] }
if(max[1] < max[0]) swap them
for (int i = 2; i < array.length(); i++) {
if(array[i] >= max[0]) max[1] = max[0]; max[0] = array[i]
else if(array[i] >= max[1]) max[1] = array[i];
}
Now, max array contains the max 2 elements.
create a temporary array of size 3,
copy first 3 elements there,
sort the temporary array,
replace the last one in the temporary array with the 4th element from the source array,
sort the temporary array,
replace the last one in the temporary array with the 5th element from the source array,
sort the temporary array,
etc.
Sorting array of size 3 is constant time and you do that once for each element of the source array, hence linear overall time.
Yep. You tagged this as C/C++ but you mentioned you could do it in Python. Anyway, here is the algorithm:
Create the array (obviously).
If the first item is greater than the second item, set first variable to the first item and second variable to second item. Otherwise, do vise-versa.
Loop through all the items (except the first two).
If the item from the array is greater than first variable, set second variable to first variable and first variable to the item. Else if the item is greater than second variable set second variable to the item.
The second variable is your answer.
list = [-1,6,9,2,0,2,8,10,8,-10]
if list[0] > list[1]:
first = list[0]
second = list[1]
else:
first = list[1]
second = list[0]
for i in range(2, len(list)):
if list[i] > first:
first, second = list[i], first
elif list[i] > second:
second = list[i]
print("First:", first)
print("Second:", second)
// assuming that v is the array and length is its length
int m1 = max(v[0], v[1]), m2 = min(v[0], v[1]);
for (int i=2; i<length; i++) {
if (likely(m2 >= v[i]))
continue;
if (unlikely(m1 < v[i]))
m2 = m1, m1 = v[i];
else
m2 = v[i];
}
The result you need is in m2 (likely and unlikely are macros defined as here for performance purposes, you can simply remove them if you don't need them).
I think the other answers have not accounted for the fact that in an array like [0, 1, 1], the second largest is 0 (according to the updated problem definition). Furthermore, all mentions of quickselect are not O(n) but rather O(n^2) and are doing much more work than necessary (on top of which that is a sorting algorithm which the problem statement disallowed). Here is a very similar algorithm to Simone's but updated to return the second largest unique element:
def find_second(numbers):
top = numbers[0]
second = None
for num in numbers[1:]:
if second is None:
if num < top: second = num
elif num > top:
second = top
top = num
else:
if num > second:
if num > top:
second = top
top = num
elif num < top: second = num
if second is not None: return second
return top
if __name__ == '__main__':
print "The second largest is %d" % find_second([1,2,3,4,4,5,5])
// Second larger element and its position(s)
int[] tab = { 12, 1, 21, 12, 8, 8, 1 };
int[] tmp = Arrays.copyOf(tab, tab.length);
int secMax = 0;
Arrays.sort(tmp);
secMax = tmp[tmp.length - 2];
System.out.println(secMax);
List<Integer> positions = new ArrayList<>();
for (int i = 0; i < tab.length; i++) {
if (tab[i] == secMax) {
positions.add(i);
}
}
System.out.println(positions);

Python implementation for next_permutation in STL

next_permutation is a C++ function which gives the lexicographically next permutation of a string. Details about its implementation can be obtained from this really awesome post. http://wordaligned.org/articles/next-permutation
Is anyone aware of a similar implementation in Python?
Is there a direct python equivalent for STL iterators?
itertools.permutations is close; the biggest difference is it treats all items as unique rather than comparing them. It also doesn't modify the sequence in-place. Implementing std::next_permutation in Python could be a good exercise for you (use indexing on a list rather than random access iterators).
No. Python iterators are comparable to input iterators, which are an STL category, but only the tip of that iceberg. You must instead use other constructs, such as a callable for an output iterator. This breaks the nice syntax generality of C++ iterators.
Here's a straightforward Python 3 implementation of wikipedia's algorithm for generating permutations in lexicographic order:
def next_permutation(a):
"""Generate the lexicographically next permutation inplace.
https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order
Return false if there is no next permutation.
"""
# Find the largest index i such that a[i] < a[i + 1]. If no such
# index exists, the permutation is the last permutation
for i in reversed(range(len(a) - 1)):
if a[i] < a[i + 1]:
break # found
else: # no break: not found
return False # no next permutation
# Find the largest index j greater than i such that a[i] < a[j]
j = next(j for j in reversed(range(i + 1, len(a))) if a[i] < a[j])
# Swap the value of a[i] with that of a[j]
a[i], a[j] = a[j], a[i]
# Reverse sequence from a[i + 1] up to and including the final element a[n]
a[i + 1:] = reversed(a[i + 1:])
return True
It produces the same results as std::next_permutation() in C++ except it doesn't transforms the input into the lexicographically first permutation if there are no more permutations.
itertools is seems to be what you need.
An implementation for the lexicographic-ally next permutation in Python (reference)
def lexicographically_next_permutation(a):
"""
Generates the lexicographically next permutation.
Input: a permutation, called "a". This method modifies
"a" in place. Returns True if we could generate a next
permutation. Returns False if it was the last permutation
lexicographically.
"""
i = len(a) - 2
while not (i < 0 or a[i] < a[i+1]):
i -= 1
if i < 0:
return False
# else
j = len(a) - 1
while not (a[j] > a[i]):
j -= 1
a[i], a[j] = a[j], a[i] # swap
a[i+1:] = reversed(a[i+1:]) # reverse elements from position i+1 till the end of the sequence
return True
A verbose implementation of this approach on lexicographic ordering
def next_permutation(case):
for index in range(1,len(case)):
Px_index = len(case) - 1 - index
#Start travelling from the end of the Data Structure
Px = case[-index-1]
Px_1 = case[-index]
#Search for a pair where latter the is greater than prior
if Px < Px_1 :
suffix = case[-index:]
pivot = Px
minimum_greater_than_pivot_suffix_index = -1
suffix_index=0
#Find the index inside the suffix where ::: [minimum value is greater than the pivot]
for Py in suffix:
if pivot < Py:
if minimum_greater_than_pivot_suffix_index == -1 or suffix[minimum_greater_than_pivot_suffix_index] >= Py:
minimum_greater_than_pivot_suffix_index=suffix_index
suffix_index +=1
#index in the main array
minimum_greater_than_pivot_index = minimum_greater_than_pivot_suffix_index + Px_index +1
#SWAP
temp = case[minimum_greater_than_pivot_index]
case[minimum_greater_than_pivot_index] = case[Px_index]
case[Px_index] = temp
#Sort suffix
new_suffix = case[Px_index+1:]
new_suffix.sort()
#Build final Version
new_prefix = case[:Px_index+1]
next_permutation = new_prefix + new_suffix
return next_permutation
elif index == (len(case) -1):
#This means that this is at the highest possible lexicographic order
return False
#EXAMPLE EXECUTIONS
print("===INT===")
#INT LIST
case = [0, 1, 2, 5, 3, 3, 0]
print(case)
print(next_permutation(case))
print("===CHAR===")
#STRING
case_char = list("dkhc")
case = [ord(c) for c in case_char]
print(case)
case = next_permutation(case)
print(case)
case_char = [str(chr(c)) for c in case]
print(case_char)
print(''.join(case_char))
The algorithm is implemented in module more_itertools as part of function more_itertools.distinct_permutations:
Documentation;
Source code.
def next_permutation(A):
# Find the largest index i such that A[i] < A[i + 1]
for i in range(size - 2, -1, -1):
if A[i] < A[i + 1]:
break
# If no such index exists, this permutation is the last one
else:
return
# Find the largest index j greater than j such that A[i] < A[j]
for j in range(size - 1, i, -1):
if A[i] < A[j]:
break
# Swap the value of A[i] with that of A[j], then reverse the
# sequence from A[i + 1] to form the new permutation
A[i], A[j] = A[j], A[i]
A[i + 1 :] = A[: i - size : -1] # A[i + 1:][::-1]
Alternatively, if the sequence is guaranteed to contain only distinct elements, then next_permutation can be implemented using functions from module more_itertools:
import more_itertools
# raises IndexError if s is already the last permutation
def next_permutation(s):
seq = sorted(s)
n = more_itertools.permutation_index(s, seq)
return more_itertools.nth_permutation(seq, len(seq), n+1)