In reading in Chapter 14 of Jon Bentley's "Programming Pearls", 2nd Edition, I understand that heaps use a one-based array and the easiest approach in C is to declare x[n+1] and waste element x[0] (page 148).
On page 157, Jon listed the complete heapsort pseudo code:
for i = [2, n]
siftup(i)
for (i = n; i >= 2; i--)
swap(1, i)
siftdown(i - 1)
Here is an implementation in C. However, the array index starts with 0, instead of 1.
void heapSort(int numbers[], int array_size)
{
int i, temp;
// Qiang: shouldn't the stop-condition be i >= 1?
for (i = (array_size / 2)-1; i >= 0; i--)
siftDown(numbers, i, array_size);
for (i = array_size-1; i >= 1; i--)
{
// Qiang: shouldn't the swap be done with numbmers[1], instead of numbers[0]?
temp = numbers[0];
numbers[0] = numbers[i];
numbers[i] = temp;
siftDown(numbers, 0, i-1);
}
}
void siftDown(int numbers[], int root, int bottom)
{
int done, maxChild, temp;
done = 0;
while ((root*2 <= bottom) && (!done))
{
if (root*2 == bottom)
maxChild = root * 2;
else if (numbers[root * 2] > numbers[root * 2 + 1])
maxChild = root * 2;
else
maxChild = root * 2 + 1;
if (numbers[root] < numbers[maxChild])
{
temp = numbers[root];
numbers[root] = numbers[maxChild];
numbers[maxChild] = temp;
root = maxChild;
}
else
done = 1;
}
}
My worry is, if the array starts with index 0, then the following properties will not hold (as written on page 148 in Jon's book):
leftchild(i) = 2*i
rightchild(i) = 2*i+1
parent(i) = i/2
It looks to me that the properties here only hold when the i starts with 1.
What strikes me is that the analysis part in the implementation used an array starting with index 1, while the implementation part used an array starting with index 0 and didn't waste the first element.
Am I missing anything here?
Edited With help from interjay, I realized the error contained in the original implementation, which could be shown with a testing input array of {66,4,23,4,78,6,44,11,22,1,99}.
Changed the original siftDown()
function a little bit to adjust the relationship between the index of parent and those of its children:
void siftDown(int numbers[], int root, int bottom)
{
int done, maxChild, temp;
done = 0;
while ((root*2 + 1 <= bottom) && (!done))
{
if (root*2 + 1 == bottom ||
numbers[root * 2 + 1] > numbers[root * 2 + 2])
maxChild = root * 2 + 1;
else
maxChild = root * 2 + 2;
if (numbers[root] < numbers[maxChild])
{
temp = numbers[root];
numbers[root] = numbers[maxChild];
numbers[maxChild] = temp;
root = maxChild;
}
else
done = 1;
}
}
Credits go to interjay, :-)
Afterword: It looks the same error doesn't appear in the implementations in wikibooks and algorithmist. Hooray!
The heap elements can be stored starting with index 0 or index 1, the decision on which to use is up to you.
If the root element is at index 1, the mathematical relations between parent and child indices are simple as you've shown above, and for that reason many books choose to teach it that way.
If the root is at index 0, you'd get these relations instead:
leftchild(i) = 2*i+1
rightchild(i) = 2*i+2
parent(i) = (i-1) / 2
It doesn't matter which one you pick as long as you are consistent.
The C code you've shown seems incorrect to me. It starts with array index 0, but uses the parent/child relations appropriate for starting with index 1.