在阅读Jon Bentley的“编程珍珠”第2章的第14章中,我理解堆使用基于单的数组,而C中最简单的方法是声明x [n + 1]并浪费元素x [0 ](第148页)。
在第157页,Jon列出了完整的heapsort伪代码:
for i = [2, n]
siftup(i)
for (i = n; i >= 2; i--)
swap(1, i)
siftdown(i - 1)
Here是C中的一个实现。但是,数组索引从0开始,而不是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;
}
}
我担心的是,如果数组以索引0开头,那么以下属性将不会成立(如Jon的书中第148页所述):
leftchild(i) = 2*i
rightchild(i) = 2*i+1
parent(i) = i/2
在我看来,这里的属性仅在i以1开头时才会存在。
令我震惊的是implementation中的分析部分使用了以索引1开头的数组,而实现部分使用了以索引0开头的数组,并没有浪费第一个元素。
我在这里遗漏了什么吗?
被修改 在interjay的帮助下,我意识到原始实现中包含的错误,可以使用{66,4,23,4,78,6,44,11,22,1,99}的测试输入数组显示。< / p>
稍微更改了原始siftDown()
函数,以调整父级索引与其子级索引之间的关系:
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;
}
}
积分转入interjay,: - )
后记: 看起来相同的错误没有出现在wikibooks和algorithmist的实现中。万岁!
答案 0 :(得分:11)
堆元素可以从索引0或索引1开始存储,决定使用哪个是由你决定的。
如果根元素位于索引1,那么父和子索引之间的数学关系很简单,如上所示,因此很多书都选择以这种方式教授它。
如果根位于索引0处,那么您将获得这些关系:
leftchild(i) = 2*i+1
rightchild(i) = 2*i+2
parent(i) = (i-1) / 2
只要你保持一致,你选择哪一个并不重要。
您显示的C代码对我来说似乎不对。它从数组索引0开始,但使用适合从索引1开始的父/子关系。
答案 1 :(得分:2)
heapsort的可重用实现希望从根索引0开始,因此用户可以使用普通(基于0)数组。您不希望要求用户分配额外的成员并在索引1处启动数组,以便他们可以使用您的heapsort函数。您需要使用@interjay显示的修改后的父/子计算。
答案 2 :(得分:0)
回答一点旧帖,认为我的小贡献可能有助于未来的访客。
如果我错过了任何场景,专家请验证并纠正我的逻辑。
考虑 Qiang Xu 链接和 interjay 基于零的索引逻辑。 这是C#代码,并使用以下输入进行测试。
// --------------------------------------------- -------------------------------------------------- ------------------------------------------------
//输入数组:
int[] ErrCaseArry = new int[] { 66, 4, 23, 4, 78, 6, 44, 11, 22, 1, 99};
int[] GenCaseArry = new int[] { 30, 20, 40, 10, 90, 160, 140, 100, 80, 70 };
int[] NearlySortedArry = new int[] { 1, 2, 3, 4, 6, 5 };
int[] FewSortedArry1 = new int[] { 3, 2, 1, 4, 5, 6 };
int[] FewSortedArry2 = new int[] { 6, 2, 3, 1, 5, 4 };
int[] ReversedArry1 = new int[] { 6, 5, 4, 3, 2, 1 };
int[] FewDuplsArry2 = new int[] { 1, 3, 1, 2, 1, 3 };
int[] MoreDuplsArry3 = new int[] { 1, 1, 2, 2, 1, 2 };
// --------------------------------------------- -------------------------------------------------- ------------------------------------------------
public void HeapSort(int[] listToSort)
{
int LastChildIndex = listToSort.Length -1;
int parentElementIndex = ((LastChildIndex - 1)/ 2);
//1. Use this loop to Construct Heap Array (Max/Min) by using Heapify function on every node.
while (parentElementIndex >= 0) // (N - 1) / 2 to 0
{
Heapify(listToSort, parentElementIndex, LastChildIndex); // (N - 1) / 2 & Lenght - 1
parentElementIndex--;
}
//-----------------------------------------------------------------------------------------------------------------------------------------------
AppendArrayToResultString("Max Heap\t", listToSort);
//2. Heap sort algorithm takes largest element off the heap and places it at the end of an array.
// This phase continue until all the elements are placed in the array that are in sorted order.
int sortedElementIndex = listToSort.Length - 1;
//-----------------------------------------------------------------------------------------------------------------------------------------------
// In this loop get Largest Element to Zero'th postion and move to end. and reduce the loop count from Heapify Array. So that elements gets sorted from right.
while (sortedElementIndex >= 0) // (N - 1) to 1
{
// Swap the elements (root(maximum value)) of the heap with the last element of the heap
Swap(ref listToSort[0], ref listToSort[sortedElementIndex]);
// sortedElementIndex-- : Decrease the size of the heap by one so that the previous max value will stay in its proper placement
sortedElementIndex--;
if (sortedElementIndex == -1) break;
// Since largest elemented from 0 to last, Re Heapify and get the remaining largest element and place it in 0 position.
Heapify(listToSort, 0, (sortedElementIndex)); // 0 to (N - 1)
}
//-----------------------------------------------------------------------------------------------------------------------------------------------
}
//Heapify() function maintain the heap property (Max Heap or Min Heap). Can be recursive or can use iteration loop like while/for.
void Heapify(int[] listToSort, int parentIndext, int lastChildIndext)
{
//bool doneFlag = false;
int largestElementIndex = 0;
int leftChildIndex = parentIndext * 2 + 1;
int rightChildIndex = parentIndext * 2 + 2;
while (leftChildIndex <= lastChildIndext) //&& !doneFlag)
{
// If leftChild is larger than rightChild or it is the last child and there is no rightChild for this parent.
// Then consider leftChild as largestElement else consider rightChild as largestElement.
if (leftChildIndex == lastChildIndext || listToSort[leftChildIndex] > listToSort[rightChildIndex])
{
largestElementIndex = leftChildIndex;
}
else
{
largestElementIndex = rightChildIndex;
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// If largestElement is larger than parent then swap them and make parent as largestElement to continue the loop.
if (listToSort[parentIndext] < listToSort[largestElementIndex])
{
// Make largestElement as parent. And continue finding if childs (left and right) are bigger than element in largestIndex position.
Swap(ref listToSort[parentIndext], ref listToSort[largestElementIndex]);
// Repeat to continue sifting down the child now
parentIndext = largestElementIndex;
leftChildIndex = ((parentIndext * 2) + 1);
rightChildIndex = ((parentIndext * 2) + 2);
}
else
{
//doneFlag = true;
break; // Trying to avoid extra flag condition check. Or return.
}
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------
void Swap(ref int num1, ref int num2)
{
int temp = num1;
num1 = num2;
num2 = temp;
}