Checking if a vector is a min heap using recursion - c++

I'm trying to write a program that checks if a vector is a min heap. I've been looking at code from here. I understand why they use 2*i+2 to compare to n, since there's a tipping point with the index where values in the vector/array (mine uses a vector) become leaf nodes. What I don't understand is why they keep using 2*i + 1 and 2*i + 2 as the index when they call the function recursively. Shouldn't they be using i+1 to access the left node and i+2 to access the right? But I tried this and I get a segmentation fault.
bool checkMinHeap(int A[], int i, int n)
{
// if i is a leaf node, return true as every leaf node is a heap
if (2*i + 2 > n)
return true;
// if i is an internal node
// recursively check if left child is heap
bool left = (A[i] <= A[2*i + 1]) && checkMinHeap(A, 2*i + 1, n);
// recursively check if right child is heap (to avoid array out
// of bound, we first check if right child exists or not)
bool right = (2*i + 2 == n) ||
(A[i] <= A[2*i + 2] && checkMinHeap(A, 2*i + 2, n));
// return true if both left and right child are heap
return left && right;
}
Their test code:
int main()
{
int A[] = {1, 2, 3, 4, 5, 6};
int n = sizeof(A) / sizeof(int);
// start with index 0 (root of the heap)
int index = 0;
if (checkMinHeap(A, index, n))
cout << "Given array is a min heap";
else
cout << "Given array is not a min heap";
return 0;
}
My test code (returns 0, when it should return 1):
int main (void)
{
vector <int> test;
test.push_back(1);
test.push_back(2);
test.push_back(3);
test.push_back(4);
test.push_back(5);
test.push_back(9);
test.push_back(3);
test.push_back(19);
cout << isMinHeap(test,0) << endl;
}

What I don't understand is why they keep using 2*i + 1 and 2*i + 2 as the index when they call the function recursively.
For instance, your heap data structure is following one.
These values are stored in an array, say A[i], i = 0, 1, …, 7.
In this picture, the blue circles i = 3 = 2*1+1 and i = 4 = 2*1+2 are the children of the green circle i = 1
Like this, in general, the left child of a parent i has an index 2*i+1 and the right one has index 2*i+2.
This is very general child-parent relationships in binary heap maps.
This is the reason why they keep using 2*i+1 and 2*i+2 as the index when they call the function recursively.

Related

binar search function not returning # of comparisons

Given an integer and a sorted array of integers, write a binary search function named binary_search that prints the number of comparisons performed doing a binary search. The function should take 3 arguments:
the number searched for,
the array of integers, and
the number of elements in the array.
If the number searched for is not in the array then the function should return the maximum number of searches to determine the element is not in the array.
Here is an example call to the function:
#include <iostream>
using namespace std;
//Function for binary_search
int binary_search(int search_value, int lst[], int elements)
{
//Dividing the array elements to its half
int mid = elements / 2;
//Condition to check search value is less then list of mid element and return it.
if (lst[mid] > search_value)
return binary_search( elements,lst, mid);
//Condition to check search value is greater then list of mid element and return it.
else if (lst[mid] < search_value)
return binary_search( search_value,&lst[mid], (elements + 1)/2);
else
return mid;
}
int main()
{
int lst[] = {0, 1, 2, 18, 19, 20, 25};
std:cout << binary_search(20, lst, 7);
}
When I search for 20 it returns the index it is found, which is 5, instead of the number of comparisons, which should be 2.
There are a few problems with your code. First off this line:
return binary_search( elements,lst, mid);
Since your first argument is the number you are searching for, it should always be the same, search_value.
Then, the idea behind a binary search is that the number of elements is halved with each call, so you don't need to change your third argument at any point, it always is elements/2 + 1.
Finally, as mentioned in the comments, your function doesn't return the number of comparisons but rather the number "mid". Since each call does a single comparison, you essentially have to find the number of calls. So, your base case for recursion (when you find the number you are searching for) should return 1, since to find it you have done a comparison. Then you just return 1 + the number of recursive calls already made.
In the end the code should look like this,
int binary_search(int search_value, int lst[], int elements)
{
int mid = elements / 2;
if (lst[mid] > search_value)
return 1 + binary_search( search_value, lst, elements/2 + 1);
else if (lst[mid] < search_value)
return 1 + binary_search( search_value, &lst[mid], elements/2 + 1);
else
return 1;
}
I would propose the solution below:
[Demo]
#include <iostream> // cout
//Function for binary_search
int binary_search(int search_value, int lst[], int elements)
{
//Base case: not found
if (elements == 0) { return 1; }
//Dividing the array elements to its half
int mid = elements / 2;
//Condition to check search value is less then list of mid element and return it.
if (lst[mid] > search_value)
{
return 2 + binary_search(search_value, lst, mid);
}
//Condition to check search value is greater then list of mid element and return it.
else if (lst[mid] < search_value)
{
return 3 + binary_search(search_value, &lst[mid + 1], elements - mid - 1);
}
else
{
return 3;
}
}
int main()
{
int lst[] = {0, 1, 2, 18, 19, 20, 25};
std::cout << "lst: ";
for (auto&& i : lst)
{
std::cout << i << " ";
}
std::cout << "\n";
for (auto&& i : lst)
{
std::cout << "binary_search(" << i << ", lst, 7): "
<< binary_search(i, lst, 7) << "\n";
}
std::cout << "binary_search(10, lst, 7): "
<< binary_search(10, lst, 7) << "\n";
}
// Outputs:
//
// lst: 0 1 2 18 19 20 25
// binary_search(0, lst, 7): 7
// binary_search(1, lst, 7): 5
// binary_search(2, lst, 7): 8
// binary_search(18, lst, 7): 3
// binary_search(19, lst, 7): 8
// binary_search(20, lst, 7): 6
// binary_search(25, lst, 7): 9
// binary_search(10, lst, 7): 9
Explanation:
We add a base case for calls to binary_search with 0 elements. This is a safety case for calls with 0 elements, but also a base case during recursive calls when an element is not found. Since we are doing a comparison, we return 1.
if (elements == 0) { return 1; }
For values smaller than the pivot element, lst[mid], we do 2 comparisons (== 0, > search_value) plus the ones returned by the recursive call.
//Condition to check search value is less then list of mid element and return it.
if (lst[mid] > search_value)
{
return 2 + binary_search(search_value, lst, mid);
}
For values bigger than the pivot element, we do 3 comparisons (== 0, > search_value, < search_value) plus the ones returned by the recursive call.
//Condition to check search value is greater then list of mid element and return it.
else if (lst[mid] < search_value)
{
return 3 + binary_search(search_value, &lst[mid + 1], elements - mid - 1);
}
Otherwise, the value is equal to the pivot element, and we return 3 comparisons (== 0, > search_value, < search_value). This is the second base case.
else
{
return 3;
}

Count how many iterations of deletion until array is ordered

I'm trying to write a program whose input is an array of integers, and its size. This code has to delete each element which is smaller than the element to the left. We want to find number of times that we can process the array this way, until we can no longer delete any more elements.
The contents of the array after we return are unimportant - only the return value is of interest.
For example: given the array [10, 9, 7, 8, 6, 5, 3, 4, 2, 1], the function should return 2, because:
[10,9,7,8,6,5,3,4,2,1] → [10,8,4] → [10]
For example: given the array [1,2,3,4], the function should return 0, because
No element is larger than the element to its right
I want each element to remove the right element if it is more than its right element. We get a smaller array. Then we repeat this operation again. Until we get to an array in which no element can delete another element. I want to calculate the number of steps performed.
int Mafia(int n, vector <int> input_array)
{
int ptr = n;
int last_ptr = n;
int night_Count = 0;
do
{
last_ptr = ptr;
ptr = 1;
for (int i = 1; i < last_ptr; i++)
{
if (input_array[i] >= input_array[i - 1])
{
input_array[ptr++] = input_array[i];
}
}
night_Count++;
} while (last_ptr > ptr);
return night_Count - 1;
}
My code works but I want it to be faster.
Do you have any idea to make this code faster, or another way that is faster than this?
Here is a O(NlogN) solution.
The idea is to iterate over the array and keep tracking candidateKillers which could kill unvisited numbers. Then we find the killer for the current number by using binary search and update the maximum iterations if needed.
Since we iterate over the array which has N numbers and apply log(N) binary search for each number, the overall time complexity is O(NlogN).
Alogrithm
If the current number is greater or equal than the number before it, it could be a killer for numbers after it.
For each killer, we keep tracking its index idx, the number of it num and the iterations needed to reach that killer iters.
The numbers in the candidateKillers by its nature are non-increasing (see next point). Therefore we can apply binary search to find the killer of the current number, which is the one that is a) the closest to the current number b) greater than the current number. This is implemented in searchKiller.
If the current number will be killed by a number in candidateKillers with killerPos, then all candidate killers after killerPos are outdated, because those outdated killers will be killed before the numbers after the current number reach them. If the current number is greater than all candidateKillers, then all the candidateKillers can be discarded.
When we find the killer of the current number, we increase the iters of the killer by one. Because from now on, one more iteration is needed to reach that killer where the current number need to be killed first.
class Solution {
public:
int countIterations(vector<int>& array) {
if (array.size() <= 1) {
return 0;
}
int ans = 0;
vector<Killer> candidateKillers = {Killer(0, array[0], 1)};
for (auto i = 1; i < array.size(); i++) {
int curNum = array[i];
int killerPos = searchKiller(candidateKillers, curNum);
if (killerPos == -1) {
// current one is the largest so far and all candidateKillers before are outdated
candidateKillers = {Killer(i, curNum, 1)};
continue;
}
// get rid of outdated killers
int popCount = candidateKillers.size() - 1 - killerPos;
for (auto j = 0; j < popCount; j++) {
candidateKillers.pop_back();
}
Killer killer = candidateKillers[killerPos];
ans = max(killer.iters, ans);
if (curNum < array[i-1]) {
// since the killer of the current one may not even be in the list e.g., if current is 4 in [6,5,4]
if (killer.idx == i - 1) {
candidateKillers[killerPos].iters += 1;
}
} else {
candidateKillers[killerPos].iters += 1;
candidateKillers.push_back(Killer(i, curNum, 1));
}
}
return ans;
}
private:
struct Killer {
Killer(int idx, int num, int iters)
: idx(idx), num(num), iters(iters) {};
int idx;
int num;
int iters;
};
int searchKiller(vector<Killer>& candidateKillers, int n) {
int lo = 0;
int hi = candidateKillers.size() - 1;
if (candidateKillers[0].num < n) {
return -1;
}
int ans = -1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (candidateKillers[mid].num > n) {
ans = mid;
lo = mid + 1;
} else {
hi = mid - 1;
}
}
return ans;
}
};
int main() {
vector<int> array1 = {10, 9, 7, 8, 6, 5, 3, 4, 2, 1};
vector<int> array2 = {1, 2, 3, 4};
vector<int> array3 = {4, 2, 1, 2, 3, 3};
cout << Solution().countIterations(array1) << endl; // 2
cout << Solution().countIterations(array2) << endl; // 0
cout << Solution().countIterations(array3) << endl; // 4
}
You can iterate in reverse, keeping two iterators or indices and moving elements in place. You don't need to allocate a new vector or even resize existing vector. Also a minor, but can replace recursion with loop or write the code the way compiler likely to do it.
This approach is still O(n^2) worst case but it would be faster in run time.

Minimum number of jumps to reach end of the array with sequence using recursion

I have a code for "Minimum number of jumps to reach end of the array with its sequence using recursion". But I am not able to print the sequence. ( There is nothing in vector vec to print )
Any help will be appreciated.
Explanation :
I want to reach from 1st element ( i.e. 2) to
last element ( i.e. 4) of the array in minimum Jump.
How Jump will be :
1st element is 2. It means I can make upto 2 jumps in array. If I take 1st jump then I can reach 2nd element ( i.e. 3) or if I take
2nd jump then I can reach 3rd element (i.e. 1)
2nd element is 3 ,so I can make maximum 3 jumps. In 1st jump I can reach to 1 , in 2nd jump I can reach to 0 and in 3rd jump I can
reach to 4
In this way I want to reach from 1st element to last element of the array in minimum number of jumps.
So output will be like , from 1st element 2, I will jump to 3. Then from 3 I will jump to 4 (last element). So 2 Jumps. ( 2 - 3 - 4 )
#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int jump(int arr[], int n, int start, vector<int> &vec)
{
if(start == n-1) // if start is the last element in array
return 0;
if( arr[start] == 0) // if array element is 0
return 0;
vector<int> vec1 = vec;
vector<int> vec2 = vec;
int minimum = INT_MAX;
for( int i = 1 ; i <= arr[start]; i++ )
{
vec1.push_back(start);
int _jump = 1 + jump( arr, n, start+i, vec1); // considering every jump
vec = (_jump < minimum) ? vec1 : vec2;
minimum = min(minimum, _jump);
}
return minimum;
}
int main()
{
int arr[] = { 2, 3, 1, 0, 4 };
int n = sizeof(arr) / sizeof(arr[0]);
vector<int> vec;
cout << "Number of jumps " << jump(arr, n, 0, vec) << endl;
cout<<"Sequence is "<<endl;
for( auto x : vec)
cout << x <<" ";
return 0;
}
output
Number of jumps 2
Sequence is
Expected output
Number of jumps 2
Sequence is 2 3 4
Here is an example that will set a vector where each index stores the correct next step in the sequence after visiting that index. I leave it to you to code following the sequence from the first element to the end, using the result vector. I also corrected this condition if( arr[start] == 0) to return "infinity" since if we visit this element, we cannot complete the sequence.
#include<iostream>
#include<vector>
#include<climits>
using namespace std;
int jump(int arr[], int n, int start, vector<int> &vec)
{
if(start == n-1) // if start is the last element in array
return 0;
if( arr[start] == 0) // if array element is 0
return INT_MAX - n;
int minimum = INT_MAX;
int step;
for( int i = 1 ; i <= arr[start]; i++ )
{
int _jump = 1 + jump( arr, n, start+i, vec); // considering every jump
if (_jump < minimum){
minimum = _jump;
step = start + i;
}
}
vec.at(start) = step;
return minimum;
}
int main()
{
int arr[] = { 2, 3, 1, 0, 4 };
int n = sizeof(arr) / sizeof(arr[0]);
vector<int> vec(n, -1);
cout << "Number of jumps " << jump(arr, n, 0, vec) << endl;
cout<<"Vector: "<<endl;
for( auto x : vec)
cout << x <<" ";
return 0;
}
Essentially, this is the minimal fix so that the sample data would works. I have not check all edge cases. For example, one might want to print something else than the value of INT_MAX is the end is not reachable.
Problem 1
You want to output values (i.e. 2, 3, 4 in your example) and not index (0, 1, 4). Thus you must push values instead of indexes.
vec1.push_back(arr[start]);
Problem 2
if(start == n-1) // if start is the last element in array
return 0;
This will not add the final value when the end is reached. You must add last value with:
vec.push_back(arr[start]);
Problem 3
if( arr[start] == 0) // if array element is 0
return 0;
A sequence that does not reach the end, would be considered to be very good. You should return a large value. Since _jump is 1 + return value of jump, the return value should be INT_MAX - 1 and minimum should also be initialized to that value for same reason.
Alternatively, you could return other values like n too instead.
Problem 4
Finally, the following condition is incorrect:
vec = (_jump < minimum) ? vec1 : vec2;
When the condition is not verified, it is vect2 that need to be copied in vec1 since the loop uses vect1.

Does this binary recursive sorting algorithm have a name?

Today I was trying to come up with a sorting algorithm and code it for practice. The one I decided on works like this. The algorithm first picks the zeroth element (let's call this the pivot) and iterates through the list. If an element is smaller than the pivot, then it is moved to somewhere on the list left to the pivot. After it finishes parsing through the list, it splits the list into two lists: the list to the left of the pivot and the list to the right of the pivot, and then recursively repeats the task on each side.
I tried looking through common comparison sorting algorithms in Wikipedia; I found methods such as quick sort, selection sort, and insertion sort, but none of these seem to be the one I'm looking for. My code is in C++, and I'll leave it here below if you want to take a look at it.
using namespace std;
template <typename T>
void sort(vector<T>& vec){
int size = vec.size();
if (size <= 1){ //this is the most basic case
return;
}
T pivot = vec[0];
int index = 0; //to help split the list later
for (int i = 1; i < size; ++i){ //moving (or not moving) the elements
if (vec[i] < pivot){
vec.insert(vec.begin(), vec[i]);
vec.erase(vec.begin() + i + 1);
++index;
}
}
if (index == 0){ //in case the 0th element is the smallest
vec.erase(vec.begin());
sort(vec);
vec.insert(vec.begin(), pivot);
}
else if(index == size - 1){ //in case the 0th element is the largest
vec.pop_back();
sort(vec);
vec.push_back(pivot);
}
//here is the main recursive portion
vector<T> left = vector<T>(vec.begin(), vec.begin() + index);
sort(left);
vector<T> right = vector<T>(vec.begin() + index + 1, vec.end());
sort(right);
//concatenating the sorted lists together
left.push_back(pivot);
left.insert(left.end(), right.begin(), right.end());
vec = left;
}
example of it in action:
int main() {
int arr[] = {-2, 5, 3, 1, 0, 12, -2, 5, 120, 0, 12, -100};
vector<int> vec = vector<int>(arr, arr + 12);
cout << "input: \n";
print(vec);
sort(vec);
cout << "\nafter sorting: \n";
print(vec);
return 0;
}
result:
input:
-2 5 3 1 0 12 -2 5 120 0 12 -100
after sorting:
-100 -2 -2 0 0 1 3 5 5 12 12 120

How to map array elements to select binary tree nodes?

I have an array of length n. I want to sort the array elements such that my new array elements are like
arr[0] = arr[n/2]
arr[1] = arr[n/4]
arr[2] = arr[3n/4]
arr[3] = arr[n/8]
arr[4] = arr[3n/8]
arr[5] = arr[5n/8]
and so on...
What I have tried, using vectors.
#include <iostream>
#include <algorithm>
#include <vector>
bool myfunc (int l, int r)
{
int m = (l+r)/2;
return m;
}
int main()
{
std::vector<int> myvector = {3,1,20,9,7,5,6,22,17,14,4};
std::sort (myvector.begin(), myvector.end(), myfunc);
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
So, for an array for length 11, I expect
myvector[0] = arr[5]
myvector[1] = arr[2]
myvector[2] = arr[8]
myvector[3] = arr[0]
myvector[4] = arr[3]
myvector[5] = arr[6]
myvector[6] = arr[9]
myvector[7] = arr[1]
myvector[8] = arr[4]
myvector[9] = arr[7]
myvector[10] = arr[10]
My question is, what should be my function definition of myfunc, such that I get expected output
bool myfunc (int l, int r)
{
int m = (l+r)/2;
//Cant figure out this logic
}
I have tried debugger, but that definitely doesnt help in defining the function! Any clues would be appreciated.
It appears you want a binary search tree (BST) stored in array form, using the same internal represenation which is often used to store a heap.
The expected output is an array such that the one based indexes form a tree, where for any one-based index x, the left node of x is at index 2*x, and the right node of x is at index 2*x+1. Additionally, there are no gaps, meaning every member of the array is used, up to N. (It is a complete binary tree) Since c++ uses zero-based indexing, you need to be careful with this one-based index.
That way of representing a tree is very good for storing a heap data structure, but very bad for a binary search tree where you want to insert things, thus breaking the completeness, and forcing you into a very expensive rebalance.
You asked for a mapping from the sorted array index to this array format. We can build it using a recursive function. This recursive function will take exactly the same amount of work as it would have taken to build the binary tree, and in fact, it is nearly identical to how you would write that function, so this is not an optimal approach. We are doing as much work as the entire problem requires, just to come up with an intermediary step.
The special note here is that we do not want the median. We want to ensure that the left subtree forms a perfect binary tree, so that it fits in the array with no gaps. Therefore, it must have a power of 2, minus 1 nodes. The right subtree can be merely complete.
int log2(int n) {
if (n > 1)
return 1 + log2(n / 2);
return 0;
}
// current_position is the index in bst_indexes
void build_binary_tree_index_mapping(std::vector<int> &bst_indexes, int lower, int upper, int current_position=0) {
if (current_position >= bst_indexes.size())
return;
int power = log2(upper - lower);
int number = 1 << (power); // left subtree must be perfect
int root = lower + number - 1;
// fill current_position
// std::cout << current_position << " = " << root << std::endl;
bst_indexes[current_position] = root;
if (lower < root) {
// fill left subtree
int left_node_position = (current_position + 1) * 2 - 1;
build_binary_tree_index_mapping(bst_indexes, lower, root - 1, left_node_position);
}
if (root < upper) {
// fill right subtree
int right_node_position = (current_position + 1) * 2 + 1 - 1;
build_binary_tree_index_mapping(bst_indexes, root + 1, upper, right_node_position);
}
}
This gives me {7, 3, 9, 1, 5, 8, 10, 0, 2, 4, 6} as the index mapping. It differs from yours because you left spaces in the lower left of the tree, and I am ensuring that the array is completely filled, so I had to shift the bottom row over, then the BST property required reordering everything.
As a side note, in order to use this mapping, you first must sort the data, which is also about the same complexity as the whole problem.
Additionally, the sorted vector already gives you a superior way to do a binary search, using std::binary_search http://en.cppreference.com/w/cpp/algorithm/binary_search.