Question: Why do we want the loop index i in line 2 of BUILD-MAX-HEAP to decrease from ⌊length[A]/2⌋ to 1 rather than increase from 1 to ⌊length[A]/2⌋?
Algorithms: (Courtesy Introduction to Algorithms book):
I tried to show this using drawings as follows.
Approach 1: if we apply build heap the opposite way from 1 to ⌊length[A]/2⌋ of array A = <5,3,17,10,84>, we would have:
Approach 2: if we apply build heap the opposite way from ⌊length[A]/2⌋ and decrease to 1 of array A = <5,3,17,10,84>, we would have:
Problem: I see that in both cases the heap property that parent is larger than its children is maintained, so I don't see why a solution says that there would be a problem such that, "we won't be allowed to call MAX-HEAPIFY, since it will fail the condition of having the subtrees be max-heaps. That is, if we start with 1, there is no guarantee that A[2] and A[3] are roots of max-heaps."
The thing is that you can only rely on MAX_HEAPIFY to do its job right, when the subtree that is rooted at i obeys the heap property everywhere except possibly for the root value (at i) itself, which may need to sift down. The job of MAX_HEAPIFY is only to move the value of the root to its right position. It cannot fix any other violations of the heap property. If however, it is guaranteed that the rest of the tree below i is obeying the heap property, then you can be sure that the subtree at i will be a heap after MAX_HEAPIFY has run.
So if you would start with top node, then who knows what you will get... the rest of the tree is not expected to obey the heap property, so MAX_HEAPIFY will not (necessarily) deliver a heap. And it doesn't help to continue the work in a top-down fashion.
If we take the example tree and perform the forward loop alternative, then we start with a call of MAX_HEAPIFY(1) on this tree:
5
/ \
3 17
/ \
10 84
...then 5 would swap with 17 (at position 3), and then we would call MAX_HEAPIFY(3) recursively on that node, which would do nothing. So we would get:
17
/ \
3 5
/ \
10 84
Next, we call MAX_HEAPIFY(2) which will swap 3 with 84:
17
/ \
84 5
/ \
10 3
Again a recursive call follows on the node with value 3, but that will not do anything.
This was the last call of MAX_HEAPIFY in the forward loop alternative... and we can see that the value 84 had no chance to find its way all the way to the top where it belongs.
Related
30
/ \
25 20
/ \ / \
22 18 17 16
/ \ / \ /\
21 13 15 5 2 1
Above is a Max-heap created following a sequence of inserting and removing operations.
If we assume that the last operation was an insertion. What will be the possible keys that could have been the last key inserted?
I'm really confused about how we can answer the question and the justification behind the solution.
If someone could give me an explanation of the solution, I would really appreciate it.
Thank you!
A heap has both the completeness and the heap property.
completeness property: All levels of the tree are full, except the last, which is filled from the left
heap property: every parent is greater in value than its children
An insert works by appending the new value to the bottom left, so we don't violate the completeness property.
Now we have to check if the value we just added violates the heap property (i.e. its parent is smaller than it). If so we swap them. We do this until we do not violate the heap property anymore or we have reached to root.
Given your example the following inserts could have happened:
Insert(1) - we are done, no heap violation, possible
Insert(17) - we insert 17, then swap with 1, but 1 is smaller than 2, so 1 could not be a parent, not possible
Insert(20) - we insert 20, swap with 1, then swap with 17, but the first swap means 1 was a parent of 2, so not possible
Insert(30) - you get the idea
Thus the answer is only Insert(1)
Hope this helps. Also, please have a look at the Wikipedia article: https://en.wikipedia.org/wiki/Heap_(data_structure)#Implementation
Show the heap at each stage when the following numbers are inserted to an initially empty min-heap in the given order: {11, 17, 13, 4, 4, 1 }. Now, show the heap at each stage when we successively perform the deleteMin operation on the heap until it is empty.
Here is the answer/checkpoint I receive:
![1]https://imgur.com/zu47RIF
I have 2 questions please:
I don't understand when we insert element 4 the second time, why do we shift 11 to make it the right child of the old element/firstly inserted element 4? Is it because we want to satisfy a requirement of the complete binary tree, which is each node in the levels from 1 to k - 2 has exactly 2 children (k = levels of the trees, level k is the bottom-most level)?
I don't understand how we deleteMin = 1, 13 becomes the right child of the newly parent 11 (which is the left child of 4). Just a quick note that my instructor gave the class 2 ways to deleteMin. The other way is fine with me - it's just the reversed process of inserting.
Like you said, the heap shape is an "almost complete tree": all levels are complete, except the lowest level which can be incomplete to the right. Therefore, the second 4 is necessarily added to the right of 17 to preserve the heap shape:
4
/ \
11 13
/ \
17 4
After that, 4 switches places with 11 to regain the min-heap property.
Deletions are typically implemented by removing the root and putting the last (i.e., bottom-rightmost) element in its place. This preserves the heap shape. The new root is then allowed to sift down in order to regain the min-heap property. So 13 becomes the new root:
13
/ \
4 4
/ \
17 11
Then 13 switches places with either child node. It looks like they chose the right-hand child in your example.
I saw an answer here with the idea implemented in Python (not very familiar with Python) - I was looking for a more general algorithm.
EDIT:
For clarification:
Say we are given a list of integer keys: 23 44 88 12 74 32 7 39 10
That list was chosen arbitrarily. We are to create an almost complete (or complete) binary search tree from that list. There is supposed to be only one such tree...how do we find it?
A binary search tree is constructed so that all items on a node's left subtree are less than the node, and all nodes on the right subtree are greater than the node.
A complete (or almost complete) binary tree is one in which all levels except possibly the last are completely full, and the bottom level is filled to the left.
So, for example, this is an almost-complete binary search tree:
4
/ \
2 5
/ \
1 3
This is not:
3
/ \
2 4
/ \
1 5
Because the bottom level of the tree is not filled from the left.
If the number of items is one less than a power of two (i.e. 3, 7, 15, etc.), then building the tree is easy. Start by sorting the list. Then, take the middle element as the root. So if you have [1,2,3,4,5,6,7], and the root node is 4.
You do the same thing recursively for the right and left halves of the array.
If the number of items is not one less than a power of two, you have to adjust the starting point (the root node) so that the bottom row is left-filled. Note that you might have to apply that adjustment recursively, as well, whenever your subtree length is not one less than a power of two.
Since this is a homework assignment, I'll leave that for you to figure out.
A min-max heap can be useful to implement a double-ended priority queue because of its constant time find-min and find-max operations. We can also retrieve the minimum and maximum elements in the min-max heap in O(log2 n) time. Sometimes, though, we may also want to delete any node in the min-max heap, and this can be done in O(log2 n) , according to the paper which introduced min-max heaps:
...
The structure can also be generalized to support the operation Find(k) (determine the kth smallest value in the structure) in constant time and the operation Delete(k) (delete the kth smallest value in the structure) in logarithmic time, for any fixed value (or set of values) of k.
...
How exactly do I perform a deletion of the kth element on a min-max heap?
I don't consider myself an "expert" in the fields of algorithms and data structures, but I do have a detailed understanding of binary heaps, including the min-max heap. See, for example, my blog series on binary heaps, starting with http://blog.mischel.com/2013/09/29/a-better-way-to-do-it-the-heap/. I have a min-max implementation that I'll get around to writing about at some point.
Your solution to the problem is correct: you do indeed have to bubble up or sift down to re-adjust the heap when you delete an arbitrary node.
Deleting an arbitrary node in a min-max heap is not fundamentally different from the same operation in a max-heap or min-heap. Consider, for example, deleting an arbitrary node in a min-heap. Start with this min-heap:
0
4 1
5 6 2 3
Now if you remove the node 5 you have:
0
4 1
6 2 3
You take the last node in the heap, 3, and put it in the place where 5 was:
0
4 1
3 6 2
In this case you don't have to sift down because it's already a leaf, but it's out of place because it's smaller than its parent. You have to bubble it up to obtain:
0
3 1
4 6 2
The same rules apply for a min-max heap. You replace the element you're removing with the last item from the heap, and decrease the count. Then, you have to check to see if it needs to be bubbled up or sifted down. The only tricky part is that the logic differs depending on whether the item is on a min level or a max level.
In your example, the heap that results from the first operation (replacing 55 with 31) is invalid because 31 is smaller than 54. So you have to bubble it up the heap.
One other thing: removing an arbitrary node is indeed a log2(n) operation. However, finding the node to delete is an O(n) operation unless you have some other data structure keeping track of where nodes are in the heap. So, in general, removal of an arbitrary node is considered O(n).
What led me to develop this solution (which I'm not 100% sure is correct) is the fact that I've actually found a solution to delete any node in a min-max heap, but it's wrong.
The wrong solution is can be found here (implemented in C++) and here (implemented in Python). I'm going to present the just mentioned wrong Python's solution, which is more accessible to everyone:
The solution is the following:
def DeleteAt(self, position):
"""delete given position"""
self.heap[position] = self.heap[-1]
del(self.heap[-1])
self.TrickleDown(position)
Now, suppose we have the following min-max heap:
level 0 10
level 1 92 56
level 2 41 54 23 11
level 3 69 51 55 65 37 31
as far as I've checked this is a valid min-max heap. Now, suppose we want to delete the element 55, which in an 0-based array would be found at index 9 (if I counted correctly).
What the solution above would do is simply put the last element in the array, in this case 31, and put it at position 9:
level 0 10
level 1 92 56
level 2 41 54 23 11
level 3 69 51 31 65 37 55
it would delete the last element of the array (which is now 55), and the resulting min-max heap would look like this:
level 0 10
level 1 92 56
level 2 41 54 23 11
level 3 69 51 31 65 37
and finally it would "trickle-down" from the position (i.e. where now we have the number 31).
"tricle-down" would check if we're in an even (or min) or odd (or max) level: we're in an odd level (3), so "trickle-down" would call "trickle-down-max" starting from 31, but since 31 has no children, it stops (check the original paper above if you don't know what I'm talking about).
But if you observe that leaves the data structure in a state that is no more a min-max heap, because 54, which is at even level and therefore should be smaller than its descendants, is greater than 31, one of its descendants.
This made me think that we couldn't just look at the children of the node at position, but that we also needed to check from that position upwards, that maybe we needed to use "trickle-up" too.
In the following reasoning, let x be the element at position after we delete the element that we wanted to delete and before any fix operations has run. Let p be its parent (if any).
The idea of my algorithm is really that one, and more specifically is based on the fact that:
If x is on a odd level (like in the example above), and we exchange it with its parent p, which is on an even level, that would not break any rules/invariants of the min-max heap from the new x's position downwards.
The same reasoning (I think) can be done if the situation would be reversed, i.e., x was originally in a even position and it would be greater than its parent.
Now, if you noticed, the only thing that could need a fix is that, if x was exchange with its parent and it's now in a even (and respectively odd) position we may need to check if it's smaller (and respectively greater) than the node at the previous even (and respectively odd) level.
This of course didn't seem to be the whole solution to me, and of course I also wanted to check if the previous parent of x, i.e. p, is in a correct position.
If p, after the exchange with x, is on a odd (and respectively even) level, it means it could be smaller (and respectively greater) than any of its descendants, because it was previously in a even (and respectively odd) level. So, I thought we needed a "trickle-down" here.
Regarding the fact if p is in a correct position with respect to its ancestors, I think the reasoning would be similar to the one above (but I'm not 100% sure).
Putting this together I came up with the solution:
function DELETE(H, i):
// H is the min-max heap array
// i is the index of the node we want to delete
// I assume, for simplicity,
// it's not out of the bounds of the array
if i is the last index of H:
remove and return H[i]
else:
l = get_last_index_of(H)
swap(H, i, l)
d = delete(H, l)
// d is the element we wanted to remove initially
// and was initially at position i
// So, at index i we now have what was the last element of H
push_up(H, i)
push_down(H, i)
return d
This seems to work according to an implementation of a min-max heap that I made and that you can find here.
Note also that the solution run in O(log2 n) time, because we're just calling "push-up" and "push-down" which run in that order.
I need to find an algorithm for generating every possible permutation of a binary tree, and need to do so without using lists (this is because the tree itself carries semantics and restraints that cannot be translated into lists). I've found an algorithm that works for trees with the height of three or less, but whenever I get to greater heights, I loose one set of possible permutations per height added.
Each node carries information about its original state, so that one node can determine if all possible permutations have been tried for that node. Also, the node carries information on weather or not it's been 'swapped', i.e. if it has seen all possible permutations of it's subtree. The tree is left-centered, meaning that the right node should always (except in some cases that I don't need to cover for this algorithm) be a leaf node, while the left node is always either a leaf or a branch.
The algorithm I'm using at the moment can be described sort of like this:
if the left child node has been swapped
swap my right node with the left child nodes right node
set the left child node as 'unswapped'
if the current node is back to its original state
swap my right node with the lowest left nodes' right node
swap the lowest left nodes two childnodes
set my left node as 'unswapped'
set my left chilnode to use this as it's original state
set this node as swapped
return null
return this;
else if the left child has not been swapped
if the result of trying to permute left child is null
return the permutation of this node
else
return the permutation of the left child node
if this node has a left node and a right node that are both leaves
swap them
set this node to be 'swapped'
The desired behaviour of the algoritm would be something like this:
branch
/ |
branch 3
/ |
branch 2
/ |
0 1
branch
/ |
branch 3
/ |
branch 2
/ |
1 0 <-- first swap
branch
/ |
branch 3
/ |
branch 1 <-- second swap
/ |
2 0
branch
/ |
branch 3
/ |
branch 1
/ |
0 2 <-- third swap
branch
/ |
branch 3
/ |
branch 0 <-- fourth swap
/ |
1 2
and so on...
The structure is just completely unsuited for permutations, but since you know it's left-centered you might be able to make some assumptions that help you out.
I tried working it in a manner similar to yours, and I always got caught on the fact that you only have a binary piece of information (swapped or not) which isn't sufficient. For four leaves, you have 4! (24) possible combinations, but you only really have three branches (3 bits, 8 possible combinations) to store the swapped state information. You simply don't have a place to store this information.
But maybe you could write a traverser that goes through the tree and uses the number of leaves to determine how many swaps are needed, and then goes through those swaps systematically instead of just leaving it to the tree itself.
Something like
For each permutation
Encode the permutation as a series of swaps from the original
Run these swaps on the original tree
Do whatever processing is needed on the swapped tree
That might not be appropriate for your application, but you haven't given that many details about why you need to do it the way you're doing it. The way you're doing it now simply won't work, since factorial (the number of permutations) grows faster than exponential (the number of "swapped" bits you have). If you had 8 leaves, you would have 7 branches and 8 leaves for a total of 15 bits. There are 40320 permutation of 8 leaves, and only 32768 possible combinations of 15 bits. Mathematically, you simply cannot represent the permutations.
What is wrong with making a list of all items in the tree, use generative means to build all possible orders (see Knuth Vol 4), and then re-map them to the tree structure?