views:

336

answers:

4

I did the following algorithm involving a Binary Heap structure:

Algorithm: heapMinimum(node)
Input    : Position n
Output   : Sequence minList; containing the postions that hold the minimum value

1. minList <-- sequence
2. if parent(node) == NULL // current node is the root of the tree
3.   minList.insertLast(node)
4. if (leftchild(node).element() == node.element())
5.   concat(heapMinimum(leftchild(node), minList))
6. if(right(node).element() == node.element())
7.   concat(heapMinimum(rightChild(node), minList))
8. return minList

What the algorithm does is basically traverse a Binary Heap given its root to find and store the nodes that hold the minimum value (ie the value that matches that of the root).

Now I'm having trouble calculating the running time, in Big O notation, of my algorithm. The reason I'm getting confused is because of the recursion that is used to traverse the left and right children of each node.

All of the operations run in constant time, O(1), except concat. But how do I exactly go about in calculating the running time of such a recursive solution ?

+4  A: 

Looks like O(N) to me, where N is the number of elements. If your heap contains nothing but equal elements, all the elements will be traversed. Also, why isn't concat O(1)? As long as you are "concatenating" numbers, it should also be O(1). If somehow concat is O(N) however (from your pseudocode it looks like it is - but you should reconsider if you really need to concatenate the two returned lists), then the total time would be O(N2) worst case.

IVlad
hmm the reason I'm thinking `concat` is linear time, `O(n)`, is because it grows proportionally to the size of the two input sequences. What structure do you think I should use to get it to constant time ?
Andreas Grech
+1 looks like O(N)!
KMan
Just use a vector the size of the heap, or a linked list to save space. That way there's no concat required, you just append the result of the recursive call to the end of your list.
IVlad
+1  A: 

I assume you are talking about a binary heap?

By definition of the heap properties, you should only be recursing until you find an element larger than what the root is. However, you must also be certain that none of the other elements at the current level of the tree are the same size as the root. Essentially, this yields the rule that once you encounter an element of the heap that is greater than the root, you do not need to recurse into the element's children.

However, in the worst case, each element may be equal to the root. In this case, you must check the entire heap, which yields O(n) time, where n is the number of elements in the heap.

So to answer your question, it is O(n)

San Jacinto
As IVlad points out, if your concat isn't O(1), you're using the wrong data structure to store it. In this case, a simple linked list would work, as would a resizeable array like a vector. For large heaps, the penalty running over the concat list each time can be very inefficient. If the concat operation isn't O(1) because of the nature of the data that it stores (for instance, each element is a sequence), then you need to count that with a different variable. example: O(n * k) where n = size of heap, k = size of longest sequence.
San Jacinto
So `concat` with, for example, a doubling strategy array based vector will take constant time to concatenate two vectors together ?
Andreas Grech
@Andreas as I pointed out, the calculation depends on the type of data you wish to store. From the point of view of the insertion operation into your list, it should be O(1) (meaning you don't traverse the entire list each time you wish to concat). The specific operations underneath concat may not be O(1), thus the need to use a different variable to count this operation. If possible, you'd want to initialize that vector to have a large enough size to hold all of the data. To save space, you instead use a linked list. Is that a little more clear?
San Jacinto
If I understood correctly, he's storing positions, so a linked list of integers should do, meaning definitely O(1).
IVlad
A: 

As others have mentioned, if your concat() is O(1) [and if it's not, you can make it so] then your algorithm is O(N) in the size of the output.

However, if you used a concat() that copies your list (depending on the system, this can be easy to accidentally do), then your worst case is O(N^2) in the size of the output. A case that causes this behavior is when your minimum nodes go deep into the tree, such that your concat() keeps copying the list at each level.

Note that this depth is bounded by the depth of your heap, so if your tree is balanced, this worst case is also O(M log M) in the size of the datastructure. You can see this because the maximum number of copies is the depth of the tree.

comingstorm
A: 

I suppose you have a bug in your solution. The first check:

if parent(node) == NULL

must be removed, but the check that node != NULL must be added.

Moreover, I suggest to use list as additional parameter, where you will put the answer. So, that is my implementation:

Algorithm: heapMinimum(node, minList)
if (node != NULL)
{
   if (minList.empty() || minList.getFirst().element() == node.element())
   {
      minList.insertLast(node)

      heapMinimum(left(node),  minList)
      heapMinimum(right(node), minList)
   }
}

Assuming that adding an element to the list take O(1), we get that the function takes O(k), where k is the number of minimal values in the heap.

Enjoy.

Slava