Iterate by level over right most element of a heap - c++

Is there a way I can create a for loop such that, given any starting location the loop will subsequently iterate through the right-most element of each level? Given some heap:
If you were inside a make_heap function, you may start at (n-2)/2) which would be the node denoted by the red twelve.
Now, given some start location (n-2)/2, is it possible to iterate such that the subsequent values of the loop will be 6 -> 2 -> 0 (array location of the right most elements above the initial level, which is the red number minus one) which correspond to 14 -> 24 -> 25.
My initial implementation looks like
using std::size_t;
size_t n = last - first; // size of heap
for(size_t start = (n-2)/2;
start > 0;
start = (size_t)pow(2, (size_t)log2(start)-1))
{
std::cout << start << std::endl;
}
My thinking was that start is equal to 2log2(start)-1, which means the previous level.
However this only yields 11, 4, 2, 1 (add one for the corresponding node location in red). It in theory should be 11, 6, 2, 0. Any ideas?

Assuming your indices are 1-based, it is straight forward to compute the parent and the child nodes of a node given its index n:
you get the parent node using n / 2
you get the left child node using n * 2
you get the rigth child node using n * 2 + 1
Since arrays in C++ are 0-base you may need to strategically add/substract 1 to convert between node and array indices.
To get to the right children of the the parent nodes you'd keep the index p of the current parent node, replacing it using p = p / 2 in each direction and access the node at p * 2 + 1. Of course, if make_heap() is anything like std::make_heap() it doesn't need to do anything like that. It merely needs to "bubble up" the new node while its parent is bigger the node.

Related

Heap sort, Understanding the basics

As a disclaimer I am new to this site and therefore, do not know very well how to ask questions. Please don't be too harsh because I really am trying to just understand how some of these concepts work. If i am missing understanding near the beginning, please just tell me that so I can start from there and not waste your time with the rest. Here goes nothing. Because I think my understanding might be flawed, I poised some questions about how heaps would act in different areas, and then tried to answer them.
First, I would like help understanding how a random set of numbers added to an empty heap would look. lets say for example, I have 9, 4, 5, 3, 2, 7, 8, 7. After adding it to the heap, what would the heap look like? I can visually understand this (I think) the 9 being the root, 4 being the first left child and so on and so forth, but since this isn't a tree specifically, and is a heap, would it sort the numbers by switching them (see paragraph "if my understanding is correct") so that they are sorted in either min or max order?
Now lets say we removed the 9 from the heap (I believe the 9 would be the root), how would we respond to this change and then what would then be put into the root? I think here if 9 is the root, we would take the next largest number and copy it into the slot of the nine, while if this was a min heap and we where just removing a node at the bottom, it would just be removed no problem.
Along similar lines, what would a formula to get the parent of the heap item in the array?
--I think I understand this, If parent is at i, the left child would be at i*2 and the right child would be at i*2+1. And therefore going to find the parent, we would have to divide i/2 to find the parent. For example if we where at i=7 the parent would be i=3 because 3.5 would be truncated and if we where at point i=6 the parent would also be i=3. From this example the child at i = 7 would be right child of i = 3 while i=6 would be the left child of i = 3.
If my understanding of this is correct, then to reheapify after a new term has been added to the root I would compare the child to parent and if the child is larger, switch the terms. BUT I would need to compare the two children (if there are two) to see which one is bigger to decide which one needs to swap. This would be for a max heap and would go the other direction for a min heap.
Finally, if I where to add the root element, how would it reheapify?
After 9 is deleted, nothing becomes the root. The heapsort algorithm goes to the left child for sorting (you said 4.) Then the right child (or 5), etc. If the number is being checked is the root (we have different implementations) then 4 becomes the root, then 5, etc. If you are confused, look at this definition of heapsort, written in javascript:
var heapSort = function(array) {
var swap = function(array, firstIndex, secondIndex) {
var temp = array[firstIndex];
array[firstIndex] = array[secondIndex];
array[secondIndex] = temp;
};
var maxHeap = function(array, i) {
var l = 2 * i;
var r = l + 1;
var largest;
if (l < array.heapSize && array[l] > array[i]) {
largest = l;
} else {
largest = i;
}
if (r < array.heapSize && array[r] > array[largest]) {
largest = r;
}
if (largest !== i) {
swap(array, i, largest);
maxHeap(array, largest);
}
};
var buildHeap = function(array) {
array.heapSize = array.length;
for (var i = Math.floor(array.length / 2); i >= 0; i--) {
maxHeap(array, i);
}
};
buildHeap(array);
for (var i = array.length-1; i >= 1; i--) {
swap(array, 0, i);
array.heapSize--;
maxHeap(array, 0);
}
array.heapMaximum = function(){
return this[0];
};
array.heapExtractMax = function(){
if(this.heapSize < 1){
throw new RangeError("heap underflow");
}
var max = this[0];
this[0] = this[this.heapSize - 1];
this.heapSize--;
maxHeap(this, 1);
return max;
};
array.heapIncreaseKey = function(i, key){
if(key < this[i]){
throw new SyntaxError("new key is smaller than current key");
}
this[i] = key;
while(i > 1 && this[Math.floor(i / 2)] < this[i]){
swap(this, i, Math.floor(i / 2));
i = Math.floor(i / 2);
}
};
array.maxHeapInsert = function(key){
this.heapSize--;
this[this.heapSize] = -Infinity;
this.heapIncreaseKey(this.heapSize, key);
};
};
var a = [Math.floor(Math.random() * 100), Math.floor(Math.random() * 100), Math.floor(Math.random() * 100), Math.floor(Math.random() * 100), Math.floor(Math.random() * 100)];
heapSort(a);
document.writeln(a);
*{
font-family:monospace;
}
I actually don't know how it would reheapify, but you can see the snippet to find out.
To begin with your first question about how the heap would look. It will take on the structure of a complete binary tree. We could just walk down the list and update the tree as we see it but this will ruin the run time so there is a more clever way to do it. We want to first start by linearly going through the array and adding it to the left most open slot where the first entry in the array is the root. Then once you have an array, we want to fix the heap from the ground up. This involves looking at the highest depth of the heap and fixing it by making a swap so that the minimum is the parent. Then move up one in the depth of the tree and make the swap if either child is less than the new parent. If this is true then make the swap, however we may have broken the min property and so we must recursively move down the heap to fix the property. Once we recursively move towards the top and fix the heap at the top then we will have made the min Heap desired. Note that through some nice algebra, we can show that this will run in O(n) time.
The second question about removing 9 is not correct (as it is not the root anymore) so let's focus on removing the root node. When the root node is removed (from the tree or the first entry of the array) then we need to place something there for the tree structure and we place the left most node of the tree or the last element in the array, but as you might be thinking, this may have ruined the min-property and you are right. So once you move the left most to the root node, we have to check its children and if it is smaller than both, then we are good. Otherwise, we need to swap with the smaller and repeat this for the next set of children until it is smaller than both its children.
In an array, it is correct that we use 2i and 2i+1 as the index so just dividing by 2 will not be sufficient. We note that 2i is even and 2i+1 is odd and so we should focus on whether the index we are looking at is even or odd. However, it is correct that truncating would given the correct answer for the parent and that the decimal would result in the decision for the left and right child.
To address your final concern, we should note that when you add something to a heap that it is a complete binary tree and should be added to the left most slot and not the root. When you add something to the left most (for a min heap), we need to check if it is smaller than its parents and move it towards the root.
Additionally, building your heap with O(n) is efficient when needing to run prim's algorithm or Dijkstra's Shortest Path Algorithm.
Hope this helps - Jason

Last man standing , used circular linked list

Question : A company hiring candidates, makes them sit in a circle.
They select every second candidate and he leaves the circle (thus circle keeps getting smaller), till only 1 is left.
So, if there are 5 people, it'll be like :-
1 2 3 4 5
1 3 4 5 (2 is selected)
1 3 5 (4 is selected)
3 5 (1 is selected)
3 (3 is left, does'nt get the job!)
Jhon an oversmart guy doesn't want to be a part of this spiteful company.
Where does he stand if he knows that there are 560 people in total.
Ans : I tried to make a program where you enter n(number of candidates)
and it'll print the value of the one seat that will go unselected.
I Used circular linked list and deletion.
Please bear with me , as i am fairly new to coding .
My program works for inputs 2, 4, 8, 16, 32, 64 and so on as ans in all these is 1.
But any other input and it's not working.
#include <iostream>
using namespace std;
struct node
{
node* ptr;
int data;
}start;
int main()
{
node *start=NULL;
int n;
cout<<"Enter the number of students : ";
cin>>n;
node *temp=new node;
temp->data=1;
temp->ptr=NULL;
start=temp;
for(int x=2;x<=n;x++)
{
node* temp1=new node;
temp1->data=x;
temp->ptr=temp1;
temp1->ptr=start;
temp=temp1;
}
node* temp2=start;
do
{
cout<<temp2->data<<" ";
temp2=temp2->ptr;
}while(temp2!=start);
cout<<endl;
//delete bigins here
temp2=start;
node* temp3=temp2->ptr;
do
{
temp2->ptr=temp3->ptr;
temp3->ptr=NULL;
delete temp3;
temp2=temp2->ptr;
temp3=temp2->ptr;
}while(temp2->ptr!=start);
temp2=start;
do
{
cout<<temp2->data<<" ";
temp2=temp2->ptr;
}while(temp2!=temp3);
cout<<endl;
}
My program works for inputs 2, 4, 8, 16, 32, 64 and so on as ans in all these is 1.
This is a good observation. Actually the answer is just a small step from here.
You have n candidates, and you select 1 each time. If n is x + 2^k (with the biggest possible k), after x steps you have 2^k candidates left and the next candidate in the line is the answer. So the answer is 2x+1.
1 2 3 4 5 6 7
^ ^ ^ |
removed |
answer
Note: This exercise can be found in Concrete Mathematics: Foundation for Computer Science. I highly recommend it.
The issue lies in the core loop:
do {
temp2->ptr=temp3->ptr;
temp3->ptr=NULL;
delete temp3;
temp2=temp2->ptr;
temp3=temp2->ptr;
} while (temp2->ptr!=start);
This loop goes through the data once only: it stops when it gets to the end of the first set of removals, because it stops the first time it gets back to start. That's why you always get the answer 1, which, as you point out, is correct when the list length is a power of 2.
Rather, it should loop until there is only one node left, which will point to itself as the next node. So the last line of the do ... while loop should be:
} while (temp2->ptr != temp2)
Clearly the world has moved on: the first time I heard this puzzle it was about pirates drinking poison to determine who got the treasure!
to greatly simplify your solution, implement a "soft delete". Put a flag on your node struct called "int deleted" and initialize it to 0. Each time you want to delete a node, just set deleted = 1. Your pointer logic in your question is having problems and this gets rid of most of it.
When you're looking for the next one to delete, if the node has deleted == 1, then don't count it as one of the remaining ones, just keep going until you find the second node with deleted = 0, and set it to 1.
You don't even really need a circular list, or even a list at this point. You can just use an array of ints with values of 0 or 1. If you keep a count of how many are still around, then you can stop as soon as you get to just one remaining, otherwise you would have to traverse the whole array to make sure there are none left.
This isn't as fast, as your list never gets smaller and you're looking at a lot of deleted entries, but it's a ton simpler.
There is a small error in the second do while loop (deletion). The while statement forces the termination of loop after iterating through it once, i.e., once it reaches back the the start node, it exits. You need to change the line
while(temp2->ptr!=start);
to
while(temp2->ptr!=temp2);
Also the last do while loop seems to run into an infinite loop because of the statement just above it:
temp2 = start;
During deletion, you do not keep track of the start pointer which gets removed as soon as element 1 is deleted. Thus temp2 points to garbage then. Removing this line should fix that too.

Finding the location of a parent node in a binary tree

So I need help coming up with an expression that will always give me the location of a child's parent node in a binary tree. Here is an example of a problem my teacher will put on our exam:
"Consider a complete binary tree with exactly 10,000 nodes, implemented with an array starting at index 0 . The array is populated in order by extracting elements from the tree one level at a time from left to right. Suppose that a node has its value stored in location 4999. Where is the value stored for this node’s parent?"
My teacher did not tell us how to solve a problem like this. She just said "Draw a binary tree and find a pattern." I did just that but i could not come up with anything! please help. thanks.
The following is entirely using integer division. I.e. fractional remainders are dropped. For any given node index N, the children of that node will always be in locations 2N+1 and 2(N+1) in the same array.
Therefore, The parent of any node N > 0 in such an array will always be at index (N-1)/2.
Parent to Child Examples:
Parent 0: children 1,2
Parent 1: children 3,4
Parent 2: children 5,6
Parent 3: children 7,8
etc...
Child to Parent Examples:
Child 8 : Parent = (8-1)/2 = 7/2 = 3
Child 7 : Parent = (7-1)/2 = 6/2 = 3
Child 6 : Parent = (6-1)/2 = 5/2 = 2
Child 5 : Parent = (5-1)/2 = 4/2 = 2
So for your problem:
(4999-1)/2 = 4998/2 = 2499
Note: remember this, since you'll be using it extensively when you start coding array-based heap-sort algorithms.
Thanks for all your help guys. And I found the answer to my question!
The general algorithm for finding the location of the parent node is:
[i + (root - 1)] / 2 where i is the location of the given node and root is the location of the root. So in the given problem above the root starts at position 0. so the equation to find the parent node of any node is [i + (0 - 1)] / 2 = (i - 1) / 2
Now let's say the root started at position 3, then the equation would be [i + (3 - 1)] / 2
= (i + 2) / 2!!!! This is the algorithm I needed. Most of you helped me solve the one problem i provided but i actually needed the general solution for a binary tree whose root can start at any postions; not just at zero
It seems this is how the array elements map back to tree based on array indices
0
1 2
3 4 5 6
If so, then the parent of index n is at floor( (n - 1) / 2 ) (for n != 0)
If you do the log2 of the number requested (4999) and take the integer part it will give you the closest power of two to the number (12). It is 2^12 = 4096.
The parent of the nodes between 4096 and 2^13 - 1, are the ones between 2^11 and 2^12 - 1. And for each pair of nodes in the first range you have its parent in the second. So you can map them taking the integer part of the half of the difference (4999 - 4096) and adding it to the parent range start (2048).
So you will have floor of 903 / 2, and add it to 2048, getting 2499.
Note that I didn't make a precise calculation, take the strategy of the answer not the results.
You can put this algorithm in a mathematical expression, with a little work.
Hope it helps!
The parent node is at n/2 if n is even.
It is at (n-1)/2 if n is odd.
So you can remember it as math.ceil((n-1)/2)

Finding Depth of Binary Tree

I am having trouble understanding this maxDepth code. Any help would be appreciated. Here is the snippet example I followed.
int maxDepth(Node *&temp)
{
if(temp == NULL)
return 0;
else
{
int lchild = maxDepth(temp->left);
int rchild = maxDepth(temp->right);
if(lchild <= rchild)
return rchild+1;
else
return lchild+1;
}
}
Basically, what I understand is that the function recursively calls itself (for each left and right cases) until it reaches the last node. once it does, it returns 0 then it does 0+1. then the previous node is 1+1. then the next one is 2+1. if there is a bst with 3 left childs, int lchild will return 3. and the extra + 1 is the root. So my question is, where do all these +1 come from. it returns 0 at the last node but why does it return 0+1 etc. when it goes up the left/right child nodes? I don't understand why. I know it does it, but why?
Consider this part (of a bigger tree):
A
\
B
Now we want to calculate the depth of this treepart, so we pass pointer to A as its param.
Obviously pointer to A is not NULL, so the code has to:
call maxDepth for each of A's children (left and right branches). A->right is B, but A->left is obviously NULL (as A has no left branch)
compare these, choose the greatest value
return this chosen value + 1 (as A itself takes a level, doesn't it?)
Now we're going to look at how maxDepth(NULL) and maxDepth(B) are calculated.
The former is quite easy: the first check will make maxDepth return 0. If the other child were NULL too, both depths would be equal (0), and we have to return 0 + 1 for A itself.
But B is not empty; it has no branches, though, so (as we noticed) its depth is 1 (greatest of 0 for NULLs at both parts + 1 for B itself).
Now let's get back to A. maxDepth of its left branch (NULL) is 0, maxDepth of its right branch is 1. Maximum of these is 1, and we have to add 1 for A itself - so it's 2.
The point is the same steps are to be done when A is just a part of the bigger tree; the result of this calculation (2) will be used in the higher levels of maxDepth calls.
Depth is being calculated using the previous node + 1
All the ones come from this part of the code:
if(lchild <= rchild)
return rchild + 1;
else
return lchild + 1;
You add yourself +1 to the results obtained in the leaves of the tree. These ones keep adding up until you exit all the recursive calls of the function and get to the root node.
Remember in binary trees a node has at most 2 children (left and right)
It is a recursive algorithm, so it calls itself over and over.
If the temp (the node being looked at) is null, it returns 0, as this node is nothing and should not count. that is the base case.
If the node being looked at is not null, it may have children. so it gets the max depth of the left sub tree (and adds 1, for the level of the current node) and the right subtree (and adds 1 for the level of the current node). it then compares the two and returns the greater of the two.
It dives down into the two subtrees (temp->left and temp->right) and repeats the operation until it reaches nodes without children. at that point it will call maxDepth on left and right, which will be null and return 0, and then start returning back up the chain of calls.
So if you you have a chain of three nodes (say, root-left1-left2) it will get down to left2 and call maxDepth(left) and maxDepth(right). each of those return 0 (they are null). then it is back at left2. it compares, both are 0, so the greater of the two is of course 0. it returns 0+1. then we are at left1 - repeats, finds that 1 is the greater of its left n right (perhaps they are the same or it has no right child) so it returns 1+1. now we are at root, same thing, it returns 2+1 = 3, which is the depth.
Because the depth is calculated with previous node+1
To find Maximum depth in binary tree keep going left and Traveres the tree, basically perform a DFS
or
We can find the depth of the binary search tree in three different recursive ways
– using instance variables to record current depth and total depth at every level
– without using instance variables in top-bottom approach
– without using instance variables in bottom-up approach
The code snippet can be reduced to just:
int maxDepth(Node *root){
if(root){ return 1 + max( maxDepth(root->left), maxDepth(root->right)); }
return 0;
}
A good way of looking at this code is from the top down:
What would happen if the BST had no nodes? We would have root = NULL and the function would immediately return an expected depth of 0.
Now suppose the tree was populated with a number of nodes. Starting at the top, the if condition would be true for the root node. We then ask, what is the max depth of the LEFT SUB TREE and the RIGHT SUB TREE by passing the root of those sub trees to maxDepth. Both the LST and the RST of the root are one level deeper than the root, so we must add one to get the depth of the tree at root of the tree passed to the function.
i think this is the right answer
int maxDepth(Node *root){
if(root){ return 1 + max( maxDepth(root->left), maxDepth(root->right)); }
return -1;
}

Confused about definition of a 'median' when constructing a kd-Tree

Im trying to build a kd-tree for searching through a set of points, but am getting confused about the use of 'median' in the wikipedia article. For ease of use, the wikipedia article states the pseudo-code of kd-tree construction as:
function kdtree (list of points pointList, int depth)
{
if pointList is empty
return nil;
else
{
// Select axis based on depth so that axis cycles through all valid values
var int axis := depth mod k;
// Sort point list and choose median as pivot element
select median by axis from pointList;
// Create node and construct subtrees
var tree_node node;
node.location := median;
node.leftChild := kdtree(points in pointList before median, depth+1);
node.rightChild := kdtree(points in pointList after median, depth+1);
return node;
}
}
I'm getting confused about the "select median..." line, simply because I'm not quite sure what is the 'right' way to apply a median here.
As far as I know, the median of an odd-sized (sorted) list of numbers is the middle element (aka, for a list of 5 things, element number 3, or index 2 in a standard zero-based array), and the median of an even-sized array is the sum of the two 'middle' elements divided by two (aka, for a list of 6 things, the median is the sum of elements 3 and 4 - or 2 and 3, if zero-indexed - divided by 2.).
However, surely that definition does not work here as we are working with a distinct set of points? How then does one choose the correct median for an even-sized list of numbers, especially for a length 2 list?
I appreciate any and all help, thanks!
-Stephen
It appears to me that you understand the meaning of median, but you are confused with something else. What do you mean be distinct set of points?
The code presented by Wikipedia is a recursive function. You have a set of points, so you create a root node and choose a median of the set. Then you call the function recursively - for the left subtree you pass in a parameter with all the points smaller than the split-value (the median) of the original list, for the right subtree you pass in the equal and larger ones. Then for each subtree a node is created where the same thing happens. It goes like this:
First step (root node):
Original set: 1 2 3 4 5 6 7 8 9 10
Split value (median): 5.5
Second step - left subtree:
Set: 1 2 3 4 5
Split value (median): 3
Second step - right subtree:
Set: 6 7 8 9 10
Split value (median): 8
Third step - left subtree of left subtree:
Set: 1 2
Split value (median): 1.5
Third step - right subtree of left subtree:
Set: 3 4 5
Split value (median): 4
Etc.
So the median is chosen for each node in the tree based on the set of numbers (points, data) which go into that subtree. Hope this helps.
You have to choose an axis with as many element on one side than the other. If the number of points is odd or the points are positioned in such a way that it isn't possible, just choose an axis to give an as even repartition as possible.